mirror of
https://github.com/nextcloud/server.git
synced 2026-06-09 00:32:29 -04:00
Merge pull request #58826 from nextcloud/carl/code-cleaning-storage-cache
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Waiting to run
Psalm static code analysis / static-code-analysis-security (push) Waiting to run
Psalm static code analysis / static-code-analysis-ocp (push) Waiting to run
Psalm static code analysis / static-code-analysis-ncu (push) Waiting to run
Psalm static code analysis / static-code-analysis-strict (push) Waiting to run
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Waiting to run
Psalm static code analysis / static-code-analysis-security (push) Waiting to run
Psalm static code analysis / static-code-analysis-ocp (push) Waiting to run
Psalm static code analysis / static-code-analysis-ncu (push) Waiting to run
Psalm static code analysis / static-code-analysis-strict (push) Waiting to run
refactor(cache-storage): Make Storage and StorageGlobal psalm strict
This commit is contained in:
commit
a119716a7d
9 changed files with 188 additions and 290 deletions
|
|
@ -14,19 +14,14 @@ use OCA\Files_External\Event\StorageUpdatedEvent;
|
|||
use OCA\Files_External\Lib\StorageConfig;
|
||||
use OCA\Files_External\MountConfig;
|
||||
use OCP\IGroup;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Service class to manage global external storage
|
||||
*/
|
||||
class GlobalStoragesService extends StoragesService {
|
||||
/**
|
||||
* Triggers $signal for all applicable users of the given
|
||||
* storage
|
||||
*
|
||||
* @param StorageConfig $storage storage data
|
||||
* @param string $signal signal to trigger
|
||||
*/
|
||||
protected function triggerHooks(StorageConfig $storage, $signal) {
|
||||
#[Override]
|
||||
protected function triggerHooks(StorageConfig $storage, $signal): void {
|
||||
// FIXME: Use as expression in empty once PHP 5.4 support is dropped
|
||||
$applicableUsers = $storage->getApplicableUsers();
|
||||
$applicableGroups = $storage->getApplicableGroups();
|
||||
|
|
@ -55,15 +50,8 @@ class GlobalStoragesService extends StoragesService {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers signal_create_mount or signal_delete_mount to
|
||||
* accommodate for additions/deletions in applicableUsers
|
||||
* and applicableGroups fields.
|
||||
*
|
||||
* @param StorageConfig $oldStorage old storage config
|
||||
* @param StorageConfig $newStorage new storage config
|
||||
*/
|
||||
protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage) {
|
||||
#[Override]
|
||||
protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage): void {
|
||||
// if mount point changed, it's like a deletion + creation
|
||||
if ($oldStorage->getMountPoint() !== $newStorage->getMountPoint()) {
|
||||
$this->eventDispatcher->dispatchTyped(new StorageDeletedEvent($oldStorage));
|
||||
|
|
@ -139,16 +127,13 @@ class GlobalStoragesService extends StoragesService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility type for this controller, used in validation
|
||||
*
|
||||
* @return int BackendService::VISIBILITY_* constants
|
||||
*/
|
||||
public function getVisibilityType() {
|
||||
#[Override]
|
||||
public function getVisibilityType(): int {
|
||||
return BackendService::VISIBILITY_ADMIN;
|
||||
}
|
||||
|
||||
protected function isApplicable(StorageConfig $config) {
|
||||
#[Override]
|
||||
protected function isApplicable(StorageConfig $config): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -157,16 +142,14 @@ class GlobalStoragesService extends StoragesService {
|
|||
*
|
||||
* @return StorageConfig[] map of storage id to storage config
|
||||
*/
|
||||
public function getStorageForAllUsers() {
|
||||
public function getStorageForAllUsers(): array {
|
||||
$mounts = $this->dbConfig->getAllMounts();
|
||||
$configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
|
||||
$configs = array_filter($configs, function ($config) {
|
||||
return $config instanceof StorageConfig;
|
||||
});
|
||||
|
||||
$keys = array_map(function (StorageConfig $config) {
|
||||
return $config->getId();
|
||||
}, $configs);
|
||||
$keys = array_map(static fn (StorageConfig $config) => $config->getId(), $configs);
|
||||
|
||||
return array_combine($keys, $configs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,10 @@ use Psr\Log\LoggerInterface;
|
|||
|
||||
/**
|
||||
* Service class to manage external storage
|
||||
*
|
||||
* @psalm-import-type ExternalMountInfo from DBConfigService
|
||||
*/
|
||||
abstract class StoragesService {
|
||||
|
||||
/**
|
||||
* @param BackendService $backendService
|
||||
* @param DBConfigService $dbConfig
|
||||
* @param IEventDispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
protected BackendService $backendService,
|
||||
protected DBConfigService $dbConfig,
|
||||
|
|
@ -47,24 +43,21 @@ abstract class StoragesService {
|
|||
) {
|
||||
}
|
||||
|
||||
protected function readDBConfig() {
|
||||
/**
|
||||
* @return list<ExternalMountInfo>
|
||||
*/
|
||||
protected function readDBConfig(): array {
|
||||
return $this->dbConfig->getAdminMounts();
|
||||
}
|
||||
|
||||
protected function getStorageConfigFromDBMount(array $mount) {
|
||||
$applicableUsers = array_filter($mount['applicable'], function ($applicable) {
|
||||
return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER;
|
||||
});
|
||||
$applicableUsers = array_map(function ($applicable) {
|
||||
return $applicable['value'];
|
||||
}, $applicableUsers);
|
||||
protected function getStorageConfigFromDBMount(array $mount): ?StorageConfig {
|
||||
$applicableUsers = array_filter($mount['applicable'], static fn (array $applicable): bool
|
||||
=> $applicable['type'] === DBConfigService::APPLICABLE_TYPE_USER);
|
||||
$applicableUsers = array_map(static fn (array $applicable) => $applicable['value'], $applicableUsers);
|
||||
|
||||
$applicableGroups = array_filter($mount['applicable'], function ($applicable) {
|
||||
return $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP;
|
||||
});
|
||||
$applicableGroups = array_map(function ($applicable) {
|
||||
return $applicable['value'];
|
||||
}, $applicableGroups);
|
||||
$applicableGroups = array_filter($mount['applicable'], static fn (array $applicable): bool
|
||||
=> $applicable['type'] === DBConfigService::APPLICABLE_TYPE_GROUP);
|
||||
$applicableGroups = array_map(static fn (array $applicable) => $applicable['value'], $applicableGroups);
|
||||
|
||||
try {
|
||||
$config = $this->createStorage(
|
||||
|
|
@ -99,18 +92,14 @@ abstract class StoragesService {
|
|||
/**
|
||||
* Read the external storage config
|
||||
*
|
||||
* @return array map of storage id to storage config
|
||||
* @return array<int, StorageConfig> map of storage id to storage config
|
||||
*/
|
||||
protected function readConfig() {
|
||||
protected function readConfig(): array {
|
||||
$mounts = $this->readDBConfig();
|
||||
$configs = array_map([$this, 'getStorageConfigFromDBMount'], $mounts);
|
||||
$configs = array_filter($configs, function ($config) {
|
||||
return $config instanceof StorageConfig;
|
||||
});
|
||||
$configs = array_map($this->getStorageConfigFromDBMount(...), $mounts);
|
||||
$configs = array_filter($configs);
|
||||
|
||||
$keys = array_map(function (StorageConfig $config) {
|
||||
return $config->getId();
|
||||
}, $configs);
|
||||
$keys = array_map(static fn (StorageConfig $config): int => $config->getId(), $configs);
|
||||
|
||||
return array_combine($keys, $configs);
|
||||
}
|
||||
|
|
@ -139,18 +128,15 @@ abstract class StoragesService {
|
|||
|
||||
/**
|
||||
* Check whether this storage service should provide access to a storage
|
||||
*
|
||||
* @param StorageConfig $config
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function isApplicable(StorageConfig $config);
|
||||
abstract protected function isApplicable(StorageConfig $config): bool;
|
||||
|
||||
/**
|
||||
* Gets all storages, valid or not
|
||||
*
|
||||
* @return StorageConfig[] array of storage configs
|
||||
*/
|
||||
public function getAllStorages() {
|
||||
public function getAllStorages(): array {
|
||||
return $this->readConfig();
|
||||
}
|
||||
|
||||
|
|
@ -159,18 +145,16 @@ abstract class StoragesService {
|
|||
*
|
||||
* @return StorageConfig[]
|
||||
*/
|
||||
public function getStorages() {
|
||||
return array_filter($this->getAllStorages(), [$this, 'validateStorage']);
|
||||
public function getStorages(): array {
|
||||
return array_filter($this->getAllStorages(), $this->validateStorage(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate storage
|
||||
* FIXME: De-duplicate with StoragesController::validate()
|
||||
*
|
||||
* @param StorageConfig $storage
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateStorage(StorageConfig $storage) {
|
||||
protected function validateStorage(StorageConfig $storage): bool {
|
||||
/** @var Backend */
|
||||
$backend = $storage->getBackend();
|
||||
/** @var AuthMechanism */
|
||||
|
|
@ -180,25 +164,19 @@ abstract class StoragesService {
|
|||
// not permitted to use backend
|
||||
return false;
|
||||
}
|
||||
if (!$authMechanism->isVisibleFor($this->getVisibilityType())) {
|
||||
// not permitted to use auth mechanism
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// permitted to use auth mechanism
|
||||
return $authMechanism->isVisibleFor($this->getVisibilityType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility type for this controller, used in validation
|
||||
*
|
||||
* @return int BackendService::VISIBILITY_* constants
|
||||
* @return BackendService::VISIBILITY_*
|
||||
*/
|
||||
abstract public function getVisibilityType();
|
||||
abstract public function getVisibilityType(): int;
|
||||
|
||||
/**
|
||||
* @return integer
|
||||
*/
|
||||
protected function getType() {
|
||||
protected function getType(): int {
|
||||
return DBConfigService::MOUNT_TYPE_ADMIN;
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +187,7 @@ abstract class StoragesService {
|
|||
*
|
||||
* @return StorageConfig storage config, with added id
|
||||
*/
|
||||
public function addStorage(StorageConfig $newStorage) {
|
||||
public function addStorage(StorageConfig $newStorage): StorageConfig {
|
||||
$allStorages = $this->readConfig();
|
||||
|
||||
$configId = $this->dbConfig->addMount(
|
||||
|
|
@ -275,7 +253,7 @@ abstract class StoragesService {
|
|||
?array $applicableUsers = null,
|
||||
?array $applicableGroups = null,
|
||||
?int $priority = null,
|
||||
) {
|
||||
): StorageConfig {
|
||||
$backend = $this->backendService->getBackend($backendIdentifier);
|
||||
if (!$backend) {
|
||||
$backend = new InvalidBackend($backendIdentifier);
|
||||
|
|
@ -313,7 +291,7 @@ abstract class StoragesService {
|
|||
* @param string $mountType hook mount type param
|
||||
* @param array $applicableArray array of applicable users/groups for which to trigger the hook
|
||||
*/
|
||||
protected function triggerApplicableHooks($signal, $mountPoint, $mountType, $applicableArray): void {
|
||||
protected function triggerApplicableHooks(string $signal, string $mountPoint, string $mountType, array $applicableArray): void {
|
||||
$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent(null));
|
||||
foreach ($applicableArray as $applicable) {
|
||||
Util::emitHook(
|
||||
|
|
@ -335,7 +313,7 @@ abstract class StoragesService {
|
|||
* @param StorageConfig $storage storage data
|
||||
* @param string $signal signal to trigger
|
||||
*/
|
||||
abstract protected function triggerHooks(StorageConfig $storage, $signal);
|
||||
abstract protected function triggerHooks(StorageConfig $storage, string $signal): void;
|
||||
|
||||
/**
|
||||
* Triggers signal_create_mount or signal_delete_mount to
|
||||
|
|
@ -345,7 +323,7 @@ abstract class StoragesService {
|
|||
* @param StorageConfig $oldStorage old storage data
|
||||
* @param StorageConfig $newStorage new storage data
|
||||
*/
|
||||
abstract protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage);
|
||||
abstract protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage): void;
|
||||
|
||||
/**
|
||||
* Update storage to the configuration
|
||||
|
|
@ -355,7 +333,7 @@ abstract class StoragesService {
|
|||
* @return StorageConfig storage config
|
||||
* @throws NotFoundException if the given storage does not exist in the config
|
||||
*/
|
||||
public function updateStorage(StorageConfig $updatedStorage) {
|
||||
public function updateStorage(StorageConfig $updatedStorage): StorageConfig {
|
||||
$id = $updatedStorage->getId();
|
||||
|
||||
$existingMount = $this->dbConfig->getMountById($id);
|
||||
|
|
@ -435,7 +413,7 @@ abstract class StoragesService {
|
|||
*
|
||||
* @throws NotFoundException if no storage was found with the given id
|
||||
*/
|
||||
public function removeStorage(int $id) {
|
||||
public function removeStorage(int $id): void {
|
||||
$existingMount = $this->dbConfig->getMountById($id);
|
||||
|
||||
if (!is_array($existingMount)) {
|
||||
|
|
@ -454,29 +432,6 @@ abstract class StoragesService {
|
|||
$this->updateOverwriteHomeFolders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the storage implementation
|
||||
*
|
||||
* @param StorageConfig $storageConfig
|
||||
* @return int
|
||||
*/
|
||||
private function getStorageId(StorageConfig $storageConfig) {
|
||||
try {
|
||||
$class = $storageConfig->getBackend()->getStorageClass();
|
||||
/** @var \OC\Files\Storage\Storage $storage */
|
||||
$storage = new $class($storageConfig->getBackendOptions());
|
||||
|
||||
// auth mechanism should fire first
|
||||
$storage = $storageConfig->getBackend()->wrapStorage($storage);
|
||||
$storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
|
||||
|
||||
/** @var \OC\Files\Storage\Storage $storage */
|
||||
return $storage->getStorageCache()->getNumericId();
|
||||
} catch (\Exception $e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateOverwriteHomeFolders(): void {
|
||||
$appIdsList = $this->appConfig->getValueArray(FilesApplication::APP_ID, ConfigLexicon::OVERWRITES_HOME_FOLDERS);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use OCP\IAppConfig;
|
|||
use OCP\IGroupManager;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Service class to read global storages applicable to the user
|
||||
|
|
@ -33,20 +34,8 @@ class UserGlobalStoragesService extends GlobalStoragesService {
|
|||
$this->userSession = $userSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace config hash ID with real IDs, for migrating legacy storages
|
||||
*
|
||||
* @param StorageConfig[] $storages Storages with real IDs
|
||||
* @param StorageConfig[] $storagesWithConfigHash Storages with config hash IDs
|
||||
*/
|
||||
protected function setRealStorageIds(array &$storages, array $storagesWithConfigHash) {
|
||||
// as a read-only view, storage IDs don't need to be real
|
||||
foreach ($storagesWithConfigHash as $storage) {
|
||||
$storages[$storage->getId()] = $storage;
|
||||
}
|
||||
}
|
||||
|
||||
protected function readDBConfig() {
|
||||
#[Override]
|
||||
protected function readDBConfig(): array {
|
||||
$userMounts = $this->dbConfig->getAdminMountsFor(DBConfigService::APPLICABLE_TYPE_USER, $this->getUser()->getUID());
|
||||
$globalMounts = $this->dbConfig->getAdminMountsFor(DBConfigService::APPLICABLE_TYPE_GLOBAL, null);
|
||||
$groups = $this->groupManager->getUserGroupIds($this->getUser());
|
||||
|
|
@ -58,18 +47,18 @@ class UserGlobalStoragesService extends GlobalStoragesService {
|
|||
return array_merge($userMounts, $groupMounts, $globalMounts);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addStorage(StorageConfig $newStorage): never {
|
||||
throw new \DomainException('UserGlobalStoragesService writing disallowed');
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function updateStorage(StorageConfig $updatedStorage): never {
|
||||
throw new \DomainException('UserGlobalStoragesService writing disallowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $id
|
||||
*/
|
||||
public function removeStorage($id): never {
|
||||
#[Override]
|
||||
public function removeStorage(int $id): never {
|
||||
throw new \DomainException('UserGlobalStoragesService writing disallowed');
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +68,7 @@ class UserGlobalStoragesService extends GlobalStoragesService {
|
|||
*
|
||||
* @return StorageConfig[]
|
||||
*/
|
||||
public function getUniqueStorages() {
|
||||
public function getUniqueStorages(): array {
|
||||
$storages = $this->getStorages();
|
||||
|
||||
$storagesByMountpoint = [];
|
||||
|
|
@ -116,20 +105,17 @@ class UserGlobalStoragesService extends GlobalStoragesService {
|
|||
* @param StorageConfig $storage
|
||||
* @return int
|
||||
*/
|
||||
protected function getPriorityType(StorageConfig $storage) {
|
||||
protected function getPriorityType(StorageConfig $storage): int {
|
||||
$applicableUsers = $storage->getApplicableUsers();
|
||||
$applicableGroups = $storage->getApplicableGroups();
|
||||
|
||||
if ($applicableUsers && $applicableUsers[0] !== 'all') {
|
||||
return 2;
|
||||
}
|
||||
if ($applicableGroups) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return $applicableGroups ? 1 : 0;
|
||||
}
|
||||
|
||||
protected function isApplicable(StorageConfig $config) {
|
||||
protected function isApplicable(StorageConfig $config): bool {
|
||||
$applicableUsers = $config->getApplicableUsers();
|
||||
$applicableGroups = $config->getApplicableGroups();
|
||||
|
||||
|
|
@ -155,7 +141,7 @@ class UserGlobalStoragesService extends GlobalStoragesService {
|
|||
* @param IUser|null $user user to get the storages for, if not set the currently logged in user will be used
|
||||
* @return StorageConfig[] array of storage configs
|
||||
*/
|
||||
public function getAllStoragesForUser(?IUser $user = null) {
|
||||
public function getAllStoragesForUser(?IUser $user = null): array {
|
||||
if (is_null($user)) {
|
||||
$user = $this->getUser();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ use OCA\Files_External\Event\StorageCreatedEvent;
|
|||
use OCA\Files_External\Event\StorageDeletedEvent;
|
||||
use OCA\Files_External\Lib\StorageConfig;
|
||||
use OCA\Files_External\MountConfig;
|
||||
use OCA\Files_External\NotFoundException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IUserSession;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Service class to manage user external storage
|
||||
|
|
@ -38,7 +38,8 @@ class UserStoragesService extends StoragesService {
|
|||
parent::__construct($backendService, $dbConfig, $eventDispatcher, $appConfig);
|
||||
}
|
||||
|
||||
protected function readDBConfig() {
|
||||
#[Override]
|
||||
protected function readDBConfig(): array {
|
||||
return $this->dbConfig->getUserMountsFor(DBConfigService::APPLICABLE_TYPE_USER, $this->getUser()->getUID());
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ class UserStoragesService extends StoragesService {
|
|||
* @param StorageConfig $storage storage data
|
||||
* @param string $signal signal to trigger
|
||||
*/
|
||||
protected function triggerHooks(StorageConfig $storage, $signal) {
|
||||
protected function triggerHooks(StorageConfig $storage, string $signal): void {
|
||||
$user = $this->getUser()->getUID();
|
||||
|
||||
// trigger hook for the current user
|
||||
|
|
@ -69,7 +70,7 @@ class UserStoragesService extends StoragesService {
|
|||
* @param StorageConfig $oldStorage old storage data
|
||||
* @param StorageConfig $newStorage new storage data
|
||||
*/
|
||||
protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage) {
|
||||
protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage): void {
|
||||
// if mount point changed, it's like a deletion + creation
|
||||
if ($oldStorage->getMountPoint() !== $newStorage->getMountPoint()) {
|
||||
$this->eventDispatcher->dispatchTyped(new StorageDeletedEvent($oldStorage));
|
||||
|
|
@ -79,31 +80,19 @@ class UserStoragesService extends StoragesService {
|
|||
}
|
||||
}
|
||||
|
||||
protected function getType() {
|
||||
#[Override]
|
||||
protected function getType(): int {
|
||||
return DBConfigService::MOUNT_TYPE_PERSONAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new storage to the configuration
|
||||
*
|
||||
* @param StorageConfig $newStorage storage attributes
|
||||
*
|
||||
* @return StorageConfig storage config, with added id
|
||||
*/
|
||||
public function addStorage(StorageConfig $newStorage) {
|
||||
#[Override]
|
||||
public function addStorage(StorageConfig $newStorage): StorageConfig {
|
||||
$newStorage->setApplicableUsers([$this->getUser()->getUID()]);
|
||||
return parent::addStorage($newStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update storage to the configuration
|
||||
*
|
||||
* @param StorageConfig $updatedStorage storage attributes
|
||||
*
|
||||
* @return StorageConfig storage config
|
||||
* @throws NotFoundException if the given storage does not exist in the config
|
||||
*/
|
||||
public function updateStorage(StorageConfig $updatedStorage) {
|
||||
#[Override]
|
||||
public function updateStorage(StorageConfig $updatedStorage): StorageConfig {
|
||||
// verify ownership through $this->isApplicable() and otherwise throws an exception
|
||||
$this->getStorage($updatedStorage->getId());
|
||||
|
||||
|
|
@ -111,20 +100,18 @@ class UserStoragesService extends StoragesService {
|
|||
return parent::updateStorage($updatedStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visibility type for this controller, used in validation
|
||||
*
|
||||
* @return int BackendService::VISIBILITY_* constants
|
||||
*/
|
||||
#[Override]
|
||||
public function getVisibilityType(): int {
|
||||
return BackendService::VISIBILITY_PERSONAL;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function isApplicable(StorageConfig $config): bool {
|
||||
return ($config->getApplicableUsers() === [$this->getUser()->getUID()]) && $config->getType() === StorageConfig::MOUNT_TYPE_PERSONAL;
|
||||
}
|
||||
|
||||
public function removeStorage($id) {
|
||||
#[Override]
|
||||
public function removeStorage(int $id): void {
|
||||
// verify ownership through $this->isApplicable() and otherwise throws an exception
|
||||
$this->getStorage($id);
|
||||
parent::removeStorage($id);
|
||||
|
|
|
|||
|
|
@ -3440,14 +3440,6 @@
|
|||
<code><![CDATA[self::SCAN_RECURSIVE_INCOMPLETE]]></code>
|
||||
</InvalidArgument>
|
||||
</file>
|
||||
<file src="lib/private/Files/Cache/Storage.php">
|
||||
<InvalidNullableReturnType>
|
||||
<code><![CDATA[array]]></code>
|
||||
</InvalidNullableReturnType>
|
||||
<NullableReturnStatement>
|
||||
<code><![CDATA[self::getGlobalCache()->getStorageInfo($storageId)]]></code>
|
||||
</NullableReturnStatement>
|
||||
</file>
|
||||
<file src="lib/private/Files/Config/MountProviderCollection.php">
|
||||
<InvalidOperand>
|
||||
<code><![CDATA[$user]]></code>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ return (require __DIR__ . '/rector-shared.php')
|
|||
$nextcloudDir . '/lib/private/Settings/AuthorizedGroupMapper.php',
|
||||
$nextcloudDir . '/apps/settings/lib/Service/AuthorizedGroupService.php',
|
||||
$nextcloudDir . '/lib/private/Files/Storage/Storage.php',
|
||||
$nextcloudDir . '/lib/private/Files/Cache/Storage.php',
|
||||
$nextcloudDir . '/lib/private/Files/Cache/StorageGlobal.php',
|
||||
$nextcloudDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php',
|
||||
$nextcloudDir . '/build/psalm/ITypedQueryBuilderTest.php',
|
||||
$nextcloudDir . '/lib/private/DB/QueryBuilder/TypedQueryBuilder.php',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
|
|
@ -7,6 +9,7 @@
|
|||
*/
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OC\DB\Exceptions\DbalException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use OCP\IDBConnection;
|
||||
|
|
@ -25,55 +28,71 @@ use Psr\Log\LoggerInterface;
|
|||
* @package OC\Files\Cache
|
||||
*/
|
||||
class Storage {
|
||||
/** @var StorageGlobal|null */
|
||||
private static $globalCache = null;
|
||||
private $storageId;
|
||||
private $numericId;
|
||||
private static ?StorageGlobal $globalCache = null;
|
||||
|
||||
/**
|
||||
* @return StorageGlobal
|
||||
*/
|
||||
public static function getGlobalCache() {
|
||||
private string $storageId;
|
||||
|
||||
private int $numericId;
|
||||
|
||||
public static function getGlobalCache(): StorageGlobal {
|
||||
if (is_null(self::$globalCache)) {
|
||||
self::$globalCache = new StorageGlobal(Server::get(IDBConnection::class));
|
||||
}
|
||||
|
||||
return self::$globalCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \OC\Files\Storage\Storage|string $storage
|
||||
* @param bool $isAvailable
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct($storage, $isAvailable, IDBConnection $connection) {
|
||||
if ($storage instanceof IStorage) {
|
||||
$this->storageId = $storage->getId();
|
||||
} else {
|
||||
$this->storageId = $storage;
|
||||
}
|
||||
public function __construct(
|
||||
IStorage|string $storage,
|
||||
bool $isAvailable,
|
||||
IDBConnection $connection,
|
||||
) {
|
||||
$this->storageId = $storage instanceof IStorage ? $storage->getId() : $storage;
|
||||
|
||||
$this->storageId = self::adjustStorageId($this->storageId);
|
||||
|
||||
if ($row = self::getStorageById($this->storageId)) {
|
||||
$this->numericId = (int)$row['numeric_id'];
|
||||
$this->numericId = $row['numeric_id'];
|
||||
} else {
|
||||
$available = $isAvailable ? 1 : 0;
|
||||
if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $available])) {
|
||||
$this->numericId = $connection->lastInsertId('*PREFIX*storages');
|
||||
} else {
|
||||
if ($row = self::getStorageById($this->storageId)) {
|
||||
$this->numericId = (int)$row['numeric_id'];
|
||||
} else {
|
||||
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId);
|
||||
|
||||
try {
|
||||
$qb = $connection->getQueryBuilder();
|
||||
$qb->insert('storages')
|
||||
->values([
|
||||
'id' => $qb->createNamedParameter($this->storageId),
|
||||
'available' => $qb->createNamedParameter($available),
|
||||
])
|
||||
->executeStatement();
|
||||
$this->numericId = $qb->getLastInsertId();
|
||||
} catch (DbalException $e) {
|
||||
if ($e->getReason() === DbalException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
||||
$qb = $connection->getQueryBuilder();
|
||||
$result = $qb->select('numeric_id')
|
||||
->from('storages')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($this->storageId)))
|
||||
->executeQuery();
|
||||
/** @var false|string|int $row */
|
||||
$row = $result->fetchOne();
|
||||
if ($row === false) {
|
||||
throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId, $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$this->numericId = (int)$row;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $storageId
|
||||
* @return array
|
||||
* @return array{id: string, numeric_id: int, available: bool, last_checked: int}|null
|
||||
*/
|
||||
public static function getStorageById($storageId) {
|
||||
public static function getStorageById(string $storageId): ?array {
|
||||
return self::getGlobalCache()->getStorageInfo($storageId);
|
||||
}
|
||||
|
||||
|
|
@ -83,26 +102,24 @@ class Storage {
|
|||
* @return string unchanged $storageId if its length is less than 64 characters,
|
||||
* else returns the md5 of $storageId
|
||||
*/
|
||||
public static function adjustStorageId($storageId) {
|
||||
public static function adjustStorageId(string $storageId): string {
|
||||
if (strlen($storageId) > 64) {
|
||||
return md5($storageId);
|
||||
}
|
||||
|
||||
return $storageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the numeric id for the storage
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumericId() {
|
||||
public function getNumericId(): int {
|
||||
return $this->numericId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string id for the storage
|
||||
*
|
||||
* @param int $numericId
|
||||
* @return string|null either the storage id string or null if the numeric id is not known
|
||||
*/
|
||||
public static function getStorageId(int $numericId): ?string {
|
||||
|
|
@ -110,44 +127,27 @@ class Storage {
|
|||
return $storage['id'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the numeric of the storage with the provided string id
|
||||
*
|
||||
* @param $storageId
|
||||
* @return int|null either the numeric storage id or null if the storage id is not known
|
||||
*/
|
||||
public static function getNumericStorageId($storageId) {
|
||||
$storageId = self::adjustStorageId($storageId);
|
||||
|
||||
if ($row = self::getStorageById($storageId)) {
|
||||
return (int)$row['numeric_id'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{available: bool, last_checked: int}
|
||||
*/
|
||||
public function getAvailability() {
|
||||
public function getAvailability(): array {
|
||||
if ($row = self::getStorageById($this->storageId)) {
|
||||
return [
|
||||
'available' => (int)$row['available'] === 1,
|
||||
'last_checked' => $row['last_checked']
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'available' => true,
|
||||
'last_checked' => time(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'available' => true,
|
||||
'last_checked' => time(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isAvailable
|
||||
* @param int $delay amount of seconds to delay reconsidering that storage further
|
||||
*/
|
||||
public function setAvailability($isAvailable, int $delay = 0) {
|
||||
public function setAvailability(bool $isAvailable, int $delay = 0): void {
|
||||
$available = $isAvailable ? 1 : 0;
|
||||
if (!$isAvailable) {
|
||||
Server::get(LoggerInterface::class)->info('Storage with ' . $this->storageId . ' marked as unavailable', ['app' => 'lib']);
|
||||
|
|
@ -162,43 +162,9 @@ class Storage {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if a string storage id is known
|
||||
*
|
||||
* @param string $storageId
|
||||
* @return bool
|
||||
* Remove the entry for the storage by the mount id.
|
||||
*/
|
||||
public static function exists($storageId) {
|
||||
return !is_null(self::getNumericStorageId($storageId));
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the entry for the storage
|
||||
*
|
||||
* @param string $storageId
|
||||
*/
|
||||
public static function remove($storageId) {
|
||||
$storageId = self::adjustStorageId($storageId);
|
||||
$numericId = self::getNumericStorageId($storageId);
|
||||
|
||||
$query = Server::get(IDBConnection::class)->getQueryBuilder();
|
||||
$query->delete('storages')
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($storageId)));
|
||||
$query->executeStatement();
|
||||
|
||||
if (!is_null($numericId)) {
|
||||
$query = Server::get(IDBConnection::class)->getQueryBuilder();
|
||||
$query->delete('filecache')
|
||||
->where($query->expr()->eq('storage', $query->createNamedParameter($numericId)));
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the entry for the storage by the mount id
|
||||
*
|
||||
* @param int $mountId
|
||||
*/
|
||||
public static function cleanByMountId(int $mountId) {
|
||||
public static function cleanByMountId(int $mountId): void {
|
||||
$db = Server::get(IDBConnection::class);
|
||||
|
||||
try {
|
||||
|
|
@ -213,24 +179,24 @@ class Storage {
|
|||
|
||||
$query = $db->getQueryBuilder();
|
||||
$query->delete('filecache')
|
||||
->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
$query->runAcrossAllShards();
|
||||
$query->executeStatement();
|
||||
->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
->runAcrossAllShards()
|
||||
->executeStatement();
|
||||
|
||||
$query = $db->getQueryBuilder();
|
||||
$query->delete('storages')
|
||||
->where($query->expr()->in('numeric_id', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
$query->executeStatement();
|
||||
->where($query->expr()->in('numeric_id', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
->executeStatement();
|
||||
|
||||
$query = $db->getQueryBuilder();
|
||||
$query->delete('mounts')
|
||||
->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
|
||||
$query->executeStatement();
|
||||
->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
|
||||
->executeStatement();
|
||||
|
||||
$db->commit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Exception $exception) {
|
||||
$db->rollBack();
|
||||
throw $e;
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
@ -21,35 +23,43 @@ use OCP\IDBConnection;
|
|||
* @package OC\Files\Cache
|
||||
*/
|
||||
class StorageGlobal {
|
||||
/** @var array<string, array> */
|
||||
private $cache = [];
|
||||
/** @var array<int, array> */
|
||||
private $numericIdCache = [];
|
||||
/** @var array<string, array{id: string, numeric_id: int, available: bool, last_checked: int}> */
|
||||
private array $cache = [];
|
||||
|
||||
/** @var array<int, array{id: string, numeric_id: int, available: bool, last_checked: int}> */
|
||||
private array $numericIdCache = [];
|
||||
|
||||
public function __construct(
|
||||
private IDBConnection $connection,
|
||||
private readonly IDBConnection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $storageIds
|
||||
*/
|
||||
public function loadForStorageIds(array $storageIds) {
|
||||
public function loadForStorageIds(array $storageIds): void {
|
||||
$builder = $this->connection->getQueryBuilder();
|
||||
$query = $builder->select(['id', 'numeric_id', 'available', 'last_checked'])
|
||||
->from('storages')
|
||||
->where($builder->expr()->in('id', $builder->createNamedParameter(array_values($storageIds), IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
$result = $query->executeQuery();
|
||||
while ($row = $result->fetch()) {
|
||||
$this->cache[$row['id']] = $row;
|
||||
while (($row = $result->fetch()) !== false) {
|
||||
$normalizedRow = [
|
||||
'id' => (string)$row['id'],
|
||||
'numeric_id' => (int)$row['numeric_id'],
|
||||
'available' => (bool)$row['available'],
|
||||
'last_checked' => (int)$row['last_checked'],
|
||||
];
|
||||
|
||||
$this->cache[$normalizedRow['id']] = $normalizedRow;
|
||||
}
|
||||
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $storageId
|
||||
* @return array|null
|
||||
* @return array{id: string, numeric_id: int, available: bool, last_checked: int}|null
|
||||
*/
|
||||
public function getStorageInfo(string $storageId): ?array {
|
||||
if (!isset($this->cache[$storageId])) {
|
||||
|
|
@ -62,17 +72,24 @@ class StorageGlobal {
|
|||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
if ($row) {
|
||||
$this->cache[$storageId] = $row;
|
||||
$this->numericIdCache[(int)$row['numeric_id']] = $row;
|
||||
if ($row !== false) {
|
||||
$normalizedRow = [
|
||||
'id' => (string)$row['id'],
|
||||
'numeric_id' => (int)$row['numeric_id'],
|
||||
'available' => (bool)$row['available'],
|
||||
'last_checked' => (int)$row['last_checked'],
|
||||
];
|
||||
|
||||
$this->cache[$storageId] = $normalizedRow;
|
||||
$this->numericIdCache[$normalizedRow['numeric_id']] = $normalizedRow;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->cache[$storageId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $numericId
|
||||
* @return array|null
|
||||
* @return array{id: string, numeric_id: int, available: bool, last_checked: int}|null
|
||||
*/
|
||||
public function getStorageInfoByNumericId(int $numericId): ?array {
|
||||
if (!isset($this->numericIdCache[$numericId])) {
|
||||
|
|
@ -85,15 +102,23 @@ class StorageGlobal {
|
|||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
if ($row) {
|
||||
$this->numericIdCache[$numericId] = $row;
|
||||
$this->cache[$row['id']] = $row;
|
||||
if ($row !== false) {
|
||||
$normalizedRow = [
|
||||
'id' => (string)$row['id'],
|
||||
'numeric_id' => (int)$row['numeric_id'],
|
||||
'available' => (bool)$row['available'],
|
||||
'last_checked' => (int)$row['last_checked'],
|
||||
];
|
||||
|
||||
$this->numericIdCache[$numericId] = $normalizedRow;
|
||||
$this->cache[$normalizedRow['id']] = $normalizedRow;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numericIdCache[$numericId] ?? null;
|
||||
}
|
||||
|
||||
public function clearCache() {
|
||||
public function clearCache(): void {
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
<file name="lib/private/Settings/AuthorizedGroupMapper.php"/>
|
||||
<file name="apps/settings/lib/Service/AuthorizedGroupService.php"/>
|
||||
<file name="lib/private/Files/Storage/Storage.php"/>
|
||||
<file name="lib/private/Files/Cache/Storage.php"/>
|
||||
<file name="lib/private/Files/Cache/StorageGlobal.php"/>
|
||||
<file name="lib/private/Files/Storage/Wrapper/Wrapper.php"/>
|
||||
<file name="build/psalm/ITypedQueryBuilderTest.php"/>
|
||||
<file name="lib/private/DB/QueryBuilder/TypedQueryBuilder.php"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue