mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
feat(Db): Use SnowflakeId for previews
Allow to get an id for the storing the preview on disk before inserting the preview on the DB. Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
This commit is contained in:
parent
c9b055a0d0
commit
336cc3fa35
19 changed files with 260 additions and 43 deletions
|
|
@ -26,6 +26,7 @@ use OCP\Files\IRootFolder;
|
|||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Override;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ class MovePreviewJob extends TimedJob {
|
|||
private readonly IMimeTypeDetector $mimeTypeDetector,
|
||||
private readonly IMimeTypeLoader $mimeTypeLoader,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly IGenerator $generator,
|
||||
IAppDataFactory $appDataFactory,
|
||||
) {
|
||||
parent::__construct($time);
|
||||
|
|
@ -136,6 +138,7 @@ class MovePreviewJob extends TimedJob {
|
|||
$path = $fileId . '/' . $previewFile->getName();
|
||||
/** @var SimpleFile $previewFile */
|
||||
$preview = Preview::fromPath($path, $this->mimeTypeDetector);
|
||||
$preview->setId($this->generator->nextId());
|
||||
if (!$preview) {
|
||||
$this->logger->error('Unable to import old preview at path.');
|
||||
continue;
|
||||
|
|
|
|||
44
core/Migrations/Version33000Date20251023110529.php
Normal file
44
core/Migrations/Version33000Date20251023110529.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\Attributes\ModifyColumn;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* Migrate away from auto-increment
|
||||
*/
|
||||
#[ModifyColumn(table: 'preview_locations', name: 'id', description: 'Remove auto-increment')]
|
||||
#[ModifyColumn(table: 'previews', name: 'id', description: 'Remove auto-increment')]
|
||||
#[ModifyColumn(table: 'preview_versions', name: 'id', description: 'Remove auto-increment')]
|
||||
class Version33000Date20251023110529 extends SimpleMigrationStep {
|
||||
/**
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if ($schema->hasTable('preview_locations')) {
|
||||
$schema->dropAutoincrementColumn('preview_locations', 'id');
|
||||
}
|
||||
|
||||
if ($schema->hasTable('preview_versions')) {
|
||||
$schema->dropAutoincrementColumn('preview_versions', 'id');
|
||||
}
|
||||
|
||||
if ($schema->hasTable('previews')) {
|
||||
$schema->dropAutoincrementColumn('previews', 'id');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
86
core/Migrations/Version33000Date20251023120529.php
Normal file
86
core/Migrations/Version33000Date20251023120529.php
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\Attributes\AddIndex;
|
||||
use OCP\Migration\Attributes\IndexType;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* Use unique index for preview_locations
|
||||
*/
|
||||
#[AddIndex(table: 'preview_locations', type: IndexType::UNIQUE)]
|
||||
class Version33000Date20251023120529 extends SimpleMigrationStep {
|
||||
public function __construct(
|
||||
private readonly IDBConnection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if ($schema->hasTable('preview_locations')) {
|
||||
$table = $schema->getTable('preview_locations');
|
||||
$table->addUniqueIndex(['bucket_name', 'object_store_name'], 'unique_bucket_store');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
|
||||
// This shouldn't run on a production instance, only daily
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('preview_locations');
|
||||
$result = $qb->executeQuery();
|
||||
|
||||
$set = [];
|
||||
|
||||
while ($row = $result->fetch()) {
|
||||
// Iterate over all the rows with duplicated rows
|
||||
$id = $row['id'];
|
||||
|
||||
if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
|
||||
// duplicate
|
||||
$authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('id')
|
||||
->from('preview_locations')
|
||||
->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($row['bucket_name'])))
|
||||
->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($row['object_store_name'])))
|
||||
->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($authoritativeId)));
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
while ($row = $result->fetch()) {
|
||||
// Update previews entries to the now de-duplicated id
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->update('previews')
|
||||
->set('location_id', $qb->createNamedParameter($id))
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
|
||||
$qb->executeStatement();
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->delete('preview_locations')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
|
||||
$qb->executeStatement();
|
||||
}
|
||||
break;
|
||||
}
|
||||
$set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1530,6 +1530,8 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version32000Date20250731062008' => $baseDir . '/core/Migrations/Version32000Date20250731062008.php',
|
||||
'OC\\Core\\Migrations\\Version32000Date20250806110519' => $baseDir . '/core/Migrations/Version32000Date20250806110519.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20250819110529' => $baseDir . '/core/Migrations/Version33000Date20250819110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251023110529' => $baseDir . '/core/Migrations/Version33000Date20251023110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251023120529' => $baseDir . '/core/Migrations/Version33000Date20251023120529.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\CronService' => $baseDir . '/core/Service/CronService.php',
|
||||
|
|
|
|||
|
|
@ -1571,6 +1571,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Migrations\\Version32000Date20250731062008' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250731062008.php',
|
||||
'OC\\Core\\Migrations\\Version32000Date20250806110519' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250806110519.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20250819110529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20250819110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251023110529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251023110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251023120529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251023120529.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\CronService' => __DIR__ . '/../../..' . '/core/Service/CronService.php',
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ class Dispatcher {
|
|||
try {
|
||||
$response = \call_user_func_array([$controller, $methodName], $arguments);
|
||||
} catch (\TypeError $e) {
|
||||
// Only intercept TypeErrors occuring on the first line, meaning that the invocation of the controller method failed.
|
||||
// Only intercept TypeErrors occurring on the first line, meaning that the invocation of the controller method failed.
|
||||
// Any other TypeError happens inside the controller method logic and should be logged as normal.
|
||||
if ($e->getFile() === $this->reflector->getFile() && $e->getLine() === $this->reflector->getStartLine()) {
|
||||
$this->logger->debug('Failed to call controller method: ' . $e->getMessage(), ['exception' => $e]);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ namespace OC\DB;
|
|||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Server;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class SchemaWrapper implements ISchemaWrapper {
|
||||
/** @var Connection */
|
||||
|
|
@ -131,4 +134,18 @@ class SchemaWrapper implements ISchemaWrapper {
|
|||
public function getDatabasePlatform() {
|
||||
return $this->connection->getDatabasePlatform();
|
||||
}
|
||||
|
||||
public function dropAutoincrementColumn(string $table, string $column): void {
|
||||
$tableObj = $this->schema->getTable($this->connection->getPrefix() . $table);
|
||||
$tableObj->modifyColumn('id', ['autoincrement' => false]);
|
||||
$platform = $this->getDatabasePlatform();
|
||||
if ($platform instanceof OraclePlatform) {
|
||||
try {
|
||||
$this->connection->executeStatement('DROP TRIGGER "' . $this->connection->getPrefix() . $table . '_AI_PK"');
|
||||
$this->connection->executeStatement('DROP SEQUENCE "' . $this->connection->getPrefix() . $table . '_SEQ"');
|
||||
} catch (Exception $e) {
|
||||
Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,16 @@ use OCP\Files\IMimeTypeDetector;
|
|||
/**
|
||||
* Preview entity mapped to the oc_previews and oc_preview_locations table.
|
||||
*
|
||||
* @method string getId()
|
||||
* @method void setId(string $id)
|
||||
* @method int getFileId() Get the file id of the original file.
|
||||
* @method void setFileId(int $fileId)
|
||||
* @method int getStorageId() Get the storage id of the original file.
|
||||
* @method void setStorageId(int $fileId)
|
||||
* @method int getOldFileId() Get the old location in the file-cache table, for legacy compatibility.
|
||||
* @method void setOldFileId(int $oldFileId)
|
||||
* @method int getLocationId() Get the location id in the preview_locations table. Only set when using an object store as primary storage.
|
||||
* @method void setLocationId(int $locationId)
|
||||
* @method string getLocationId() Get the location id in the preview_locations table. Only set when using an object store as primary storage.
|
||||
* @method void setLocationId(string $locationId)
|
||||
* @method string|null getBucketName() Get the bucket name where the preview is stored. This is stored in the preview_locations table.
|
||||
* @method string|null getObjectStoreName() Get the object store name where the preview is stored. This is stored in the preview_locations table.
|
||||
* @method int getWidth() Get the width of the preview.
|
||||
|
|
@ -46,7 +48,7 @@ use OCP\Files\IMimeTypeDetector;
|
|||
* @method string getEtag() Get the etag of the preview.
|
||||
* @method void setEtag(string $etag)
|
||||
* @method string|null getVersion() Get the version for files_versions_s3
|
||||
* @method void setVersionId(int $versionId)
|
||||
* @method void setVersionId(string $versionId)
|
||||
* @method bool|null getIs() Get the version for files_versions_s3
|
||||
* @method bool isEncrypted() Get whether the preview is encrypted. At the moment every preview is unencrypted.
|
||||
* @method void setEncrypted(bool $encrypted)
|
||||
|
|
@ -57,7 +59,7 @@ class Preview extends Entity {
|
|||
protected ?int $fileId = null;
|
||||
protected ?int $oldFileId = null;
|
||||
protected ?int $storageId = null;
|
||||
protected ?int $locationId = null;
|
||||
protected ?string $locationId = null;
|
||||
protected ?string $bucketName = null;
|
||||
protected ?string $objectStoreName = null;
|
||||
protected ?int $width = null;
|
||||
|
|
@ -72,14 +74,15 @@ class Preview extends Entity {
|
|||
protected ?bool $cropped = null;
|
||||
protected ?string $etag = null;
|
||||
protected ?string $version = null;
|
||||
protected ?int $versionId = null;
|
||||
protected ?string $versionId = null;
|
||||
protected ?bool $encrypted = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('id', Types::STRING);
|
||||
$this->addType('fileId', Types::BIGINT);
|
||||
$this->addType('storageId', Types::BIGINT);
|
||||
$this->addType('oldFileId', Types::BIGINT);
|
||||
$this->addType('locationId', Types::BIGINT);
|
||||
$this->addType('locationId', Types::STRING);
|
||||
$this->addType('width', Types::INTEGER);
|
||||
$this->addType('height', Types::INTEGER);
|
||||
$this->addType('mimetypeId', Types::INTEGER);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use OCP\DB\Exception;
|
|||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
|
|
@ -29,6 +30,7 @@ class PreviewMapper extends QBMapper {
|
|||
public function __construct(
|
||||
IDBConnection $db,
|
||||
private readonly IMimeTypeLoader $mimeTypeLoader,
|
||||
private readonly IGenerator $snowflake,
|
||||
) {
|
||||
parent::__construct($db, self::TABLE_NAME, Preview::class);
|
||||
}
|
||||
|
|
@ -50,13 +52,15 @@ class PreviewMapper extends QBMapper {
|
|||
|
||||
if ($preview->getVersion() !== null && $preview->getVersion() !== '') {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$id = $this->snowflake->nextId();
|
||||
$qb->insert(self::VERSION_TABLE_NAME)
|
||||
->values([
|
||||
'id' => $id,
|
||||
'version' => $preview->getVersion(),
|
||||
'file_id' => $preview->getFileId(),
|
||||
])
|
||||
->executeStatement();
|
||||
$entity->setVersionId($qb->getLastInsertId());
|
||||
$entity->setVersionId($id);
|
||||
}
|
||||
return parent::insert($preview);
|
||||
}
|
||||
|
|
@ -148,7 +152,13 @@ class PreviewMapper extends QBMapper {
|
|||
));
|
||||
}
|
||||
|
||||
public function getLocationId(string $bucket, string $objectStore): int {
|
||||
/**
|
||||
* Get the location id corresponding to the $bucket and $objectStore. Create one
|
||||
* if not existing yet.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getLocationId(string $bucket, string $objectStore): string {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$result = $qb->select('id')
|
||||
->from(self::LOCATION_TABLE_NAME)
|
||||
|
|
@ -157,14 +167,33 @@ class PreviewMapper extends QBMapper {
|
|||
->executeQuery();
|
||||
$data = $result->fetchOne();
|
||||
if ($data) {
|
||||
return $data;
|
||||
return (string)$data;
|
||||
} else {
|
||||
$qb->insert(self::LOCATION_TABLE_NAME)
|
||||
->values([
|
||||
'bucket_name' => $qb->createNamedParameter($bucket),
|
||||
'object_store_name' => $qb->createNamedParameter($objectStore),
|
||||
])->executeStatement();
|
||||
return $qb->getLastInsertId();
|
||||
try {
|
||||
$id = $this->snowflake->nextId();
|
||||
$qb->insert(self::LOCATION_TABLE_NAME)
|
||||
->values([
|
||||
'id' => $qb->createNamedParameter($id),
|
||||
'bucket_name' => $qb->createNamedParameter($bucket),
|
||||
'object_store_name' => $qb->createNamedParameter($objectStore),
|
||||
])->executeStatement();
|
||||
return $id;
|
||||
} catch (Exception $e) {
|
||||
if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
||||
// Fetch again as there seems to be another entry added meanwhile
|
||||
$result = $qb->select('id')
|
||||
->from(self::LOCATION_TABLE_NAME)
|
||||
->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($bucket)))
|
||||
->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($objectStore)))
|
||||
->executeQuery();
|
||||
$data = $result->fetchOne();
|
||||
if ($data) {
|
||||
return (string)$data;
|
||||
}
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use OCP\IPreview;
|
|||
use OCP\IStreamImage;
|
||||
use OCP\Preview\BeforePreviewFetchedEvent;
|
||||
use OCP\Preview\IVersionedPreviewFile;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Generator {
|
||||
|
|
@ -37,6 +38,7 @@ class Generator {
|
|||
private LoggerInterface $logger,
|
||||
private PreviewMapper $previewMapper,
|
||||
private StorageFactory $storageFactory,
|
||||
private IGenerator $snowflakeGenerator,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +350,7 @@ class Generator {
|
|||
|
||||
try {
|
||||
$previewEntry = new Preview();
|
||||
$previewEntry->setId($this->snowflakeGenerator->nextId());
|
||||
$previewEntry->setFileId($file->getId());
|
||||
$previewEntry->setStorageId($file->getMountPoint()->getNumericStorageId());
|
||||
$previewEntry->setSourceMimeType($file->getMimeType());
|
||||
|
|
@ -360,7 +363,6 @@ class Generator {
|
|||
$previewEntry->setMimetype($preview->dataMimeType());
|
||||
$previewEntry->setEtag($file->getEtag());
|
||||
$previewEntry->setMtime((new \DateTime())->getTimestamp());
|
||||
$previewEntry->setSize(0);
|
||||
return $this->savePreview($previewEntry, $preview);
|
||||
} catch (NotPermittedException) {
|
||||
throw new NotFoundException();
|
||||
|
|
@ -502,6 +504,7 @@ class Generator {
|
|||
}
|
||||
|
||||
$previewEntry = new Preview();
|
||||
$previewEntry->setId($this->snowflakeGenerator->nextId());
|
||||
$previewEntry->setFileId($file->getId());
|
||||
$previewEntry->setStorageId($file->getMountPoint()->getNumericStorageId());
|
||||
$previewEntry->setWidth($width);
|
||||
|
|
@ -514,7 +517,6 @@ class Generator {
|
|||
$previewEntry->setMimeType($preview->dataMimeType());
|
||||
$previewEntry->setEtag($file->getEtag());
|
||||
$previewEntry->setMtime((new \DateTime())->getTimestamp());
|
||||
$previewEntry->setSize(0);
|
||||
if ($cacheResult) {
|
||||
$previewEntry = $this->savePreview($previewEntry, $preview);
|
||||
return new PreviewFile($previewEntry, $this->storageFactory, $this->previewMapper);
|
||||
|
|
@ -530,26 +532,20 @@ class Generator {
|
|||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function savePreview(Preview $previewEntry, IImage $preview): Preview {
|
||||
$previewEntry = $this->previewMapper->insert($previewEntry);
|
||||
|
||||
// we need to save to DB first
|
||||
try {
|
||||
if ($preview instanceof IStreamImage) {
|
||||
$size = $this->storageFactory->writePreview($previewEntry, $preview->resource());
|
||||
} else {
|
||||
$stream = fopen('php://temp', 'w+');
|
||||
fwrite($stream, $preview->data());
|
||||
rewind($stream);
|
||||
$size = $this->storageFactory->writePreview($previewEntry, $stream);
|
||||
}
|
||||
if (!$size) {
|
||||
throw new \RuntimeException('Unable to write preview file');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->previewMapper->delete($previewEntry);
|
||||
throw $e;
|
||||
if ($preview instanceof IStreamImage) {
|
||||
$size = $this->storageFactory->writePreview($previewEntry, $preview->resource());
|
||||
} else {
|
||||
$stream = fopen('php://temp', 'w+');
|
||||
fwrite($stream, $preview->data());
|
||||
rewind($stream);
|
||||
$size = $this->storageFactory->writePreview($previewEntry, $stream);
|
||||
}
|
||||
if (!$size) {
|
||||
throw new \RuntimeException('Unable to write preview file');
|
||||
}
|
||||
$previewEntry->setSize($size);
|
||||
return $this->previewMapper->update($previewEntry);
|
||||
|
||||
return $this->previewMapper->insert($previewEntry);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use OCP\Files\NotPermittedException;
|
|||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Override;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RecursiveDirectoryIterator;
|
||||
|
|
@ -38,6 +39,7 @@ class LocalPreviewStorage implements IPreviewStorage {
|
|||
private readonly IDBConnection $connection,
|
||||
private readonly IMimeTypeDetector $mimeTypeDetector,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly IGenerator $generator,
|
||||
) {
|
||||
$this->instanceId = $this->config->getSystemValueString('instanceid');
|
||||
$this->rootFolder = $this->config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
|
||||
|
|
@ -118,6 +120,7 @@ class LocalPreviewStorage implements IPreviewStorage {
|
|||
$this->logger->error('Unable to parse preview information for ' . $file->getRealPath());
|
||||
continue;
|
||||
}
|
||||
$preview->setId($this->generator->nextId());
|
||||
try {
|
||||
$preview->setSize($file->getSize());
|
||||
$preview->setMtime($file->getMtime());
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use OCP\IBinaryFinder;
|
|||
use OCP\IConfig;
|
||||
use OCP\IPreview;
|
||||
use OCP\Preview\IProviderV2;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -141,6 +142,7 @@ class PreviewManager implements IPreview {
|
|||
$this->container->get(LoggerInterface::class),
|
||||
$this->container->get(PreviewMapper::class),
|
||||
$this->container->get(StorageFactory::class),
|
||||
$this->container->get(IGenerator::class),
|
||||
);
|
||||
}
|
||||
return $this->generator;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,8 @@ use function substr;
|
|||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class Entity {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
/** @var int $id */
|
||||
public $id = null;
|
||||
|
||||
private array $_updatedFields = [];
|
||||
/** @var array<string, \OCP\DB\Types::*> */
|
||||
|
|
|
|||
|
|
@ -90,4 +90,11 @@ interface ISchemaWrapper {
|
|||
* @since 23.0.0
|
||||
*/
|
||||
public function getDatabasePlatform();
|
||||
|
||||
/**
|
||||
* Drop autoincrement from an existing table of the database.
|
||||
*
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function dropAutoincrementColumn(string $table, string $column): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use OCP\IPreview;
|
|||
use OCP\Preview\BeforePreviewFetchedEvent;
|
||||
use OCP\Preview\IProviderV2;
|
||||
use OCP\Preview\IVersionedPreviewFile;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\TestWith;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
|
@ -41,6 +42,7 @@ class GeneratorTest extends TestCase {
|
|||
private LoggerInterface&MockObject $logger;
|
||||
private StorageFactory&MockObject $storageFactory;
|
||||
private PreviewMapper&MockObject $previewMapper;
|
||||
private IGenerator&MockObject $snowflakeGenerator;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
|
@ -52,6 +54,7 @@ class GeneratorTest extends TestCase {
|
|||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->previewMapper = $this->createMock(PreviewMapper::class);
|
||||
$this->storageFactory = $this->createMock(StorageFactory::class);
|
||||
$this->snowflakeGenerator = $this->createMock(IGenerator::class);
|
||||
|
||||
$this->generator = new Generator(
|
||||
$this->config,
|
||||
|
|
@ -61,6 +64,7 @@ class GeneratorTest extends TestCase {
|
|||
$this->logger,
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
$this->snowflakeGenerator,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use OCP\IAppConfig;
|
|||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Server;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use PHPUnit\Framework\Attributes\TestDox;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -123,6 +124,7 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IGenerator::class),
|
||||
Server::get(IAppDataFactory::class),
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
|
|
@ -155,6 +157,7 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IGenerator::class),
|
||||
Server::get(IAppDataFactory::class)
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
|
|
@ -195,6 +198,7 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IGenerator::class),
|
||||
Server::get(IAppDataFactory::class)
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
|
|
|
|||
|
|
@ -14,16 +14,28 @@ use OC\Preview\Db\Preview;
|
|||
use OC\Preview\Db\PreviewMapper;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Server;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use Test\TestCase;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class PreviewMapperTest extends TestCase {
|
||||
private PreviewMapper $previewMapper;
|
||||
private IDBConnection $connection;
|
||||
private IGenerator $snowflake;
|
||||
|
||||
public function setUp(): void {
|
||||
$this->previewMapper = Server::get(PreviewMapper::class);
|
||||
$this->connection = Server::get(IDBConnection::class);
|
||||
$this->snowflake = Server::get(IGenerator::class);
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->delete('preview_locations')->executeStatement();
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->delete('preview_versions')->executeStatement();
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->delete('previews')->executeStatement();
|
||||
}
|
||||
|
||||
public function testGetAvailablePreviews(): void {
|
||||
|
|
@ -51,15 +63,17 @@ class PreviewMapperTest extends TestCase {
|
|||
$locationId = null;
|
||||
if ($bucket) {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$locationId = $this->snowflake->nextId();
|
||||
$qb->insert('preview_locations')
|
||||
->values([
|
||||
'id' => $locationId,
|
||||
'bucket_name' => $qb->createNamedParameter('preview-' . $bucket),
|
||||
'object_store_name' => $qb->createNamedParameter('default'),
|
||||
]);
|
||||
$qb->executeStatement();
|
||||
$locationId = $qb->getLastInsertId();
|
||||
}
|
||||
$preview = new Preview();
|
||||
$preview->setId($this->snowflake->nextId());
|
||||
$preview->setFileId($fileId);
|
||||
$preview->setStorageId(1);
|
||||
$preview->setCropped(true);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use OC\Preview\Db\Preview;
|
|||
use OC\Preview\Db\PreviewMapper;
|
||||
use OC\Preview\PreviewService;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use OCP\Snowflake\IGenerator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(PreviewService::class)]
|
||||
|
|
@ -22,10 +22,12 @@ use PHPUnit\Framework\TestCase;
|
|||
class PreviewServiceTest extends TestCase {
|
||||
private PreviewService $previewService;
|
||||
private PreviewMapper $previewMapper;
|
||||
private IGenerator $snowflakeGenerator;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->previewService = Server::get(PreviewService::class);
|
||||
$this->previewMapper = Server::get(PreviewMapper::class);
|
||||
$this->snowflakeGenerator = Server::get(IGenerator::class);
|
||||
$this->previewService->deleteAll();
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +38,7 @@ class PreviewServiceTest extends TestCase {
|
|||
public function testGetAvailableFileIds(): void {
|
||||
foreach (range(1, 20) as $i) {
|
||||
$preview = new Preview();
|
||||
$preview->setId($this->snowflakeGenerator->nextId());
|
||||
$preview->setFileId($i % 10);
|
||||
$preview->setStorageId(1);
|
||||
$preview->setWidth($i);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level
|
||||
// when updating major/minor version number.
|
||||
|
||||
$OC_Version = [33, 0, 0, 2];
|
||||
$OC_Version = [33, 0, 0, 3];
|
||||
|
||||
// The human-readable string
|
||||
$OC_VersionString = '33.0.0 dev';
|
||||
|
|
|
|||
Loading…
Reference in a new issue