diff --git a/core/BackgroundJobs/MovePreviewJob.php b/core/BackgroundJobs/MovePreviewJob.php index 930f998a28bed..e0a9d5b5517c8 100644 --- a/core/BackgroundJobs/MovePreviewJob.php +++ b/core/BackgroundJobs/MovePreviewJob.php @@ -26,6 +26,7 @@ use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; +use OCP\Snowflake\IGenerator; use Override; use Psr\Log\LoggerInterface; @@ -44,6 +45,7 @@ public function __construct( 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 @@ private function processPreviews(int $fileId, bool $flatPath): void { $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; diff --git a/core/Migrations/Version33000Date20251023110529.php b/core/Migrations/Version33000Date20251023110529.php new file mode 100644 index 0000000000000..d87e8bc1f0b64 --- /dev/null +++ b/core/Migrations/Version33000Date20251023110529.php @@ -0,0 +1,44 @@ +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; + } +} diff --git a/core/Migrations/Version33000Date20251023120529.php b/core/Migrations/Version33000Date20251023120529.php new file mode 100644 index 0000000000000..bcc6d691a7a44 --- /dev/null +++ b/core/Migrations/Version33000Date20251023120529.php @@ -0,0 +1,86 @@ +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']; + } + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index d14d80b23f76c..e883f4198330d 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1530,6 +1530,8 @@ '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', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index fd607dfc14a06..a24c51fbf3700 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.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', diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php index ee20dee6a4541..317c6917e6c10 100644 --- a/lib/private/AppFramework/Http/Dispatcher.php +++ b/lib/private/AppFramework/Http/Dispatcher.php @@ -204,7 +204,7 @@ private function executeController(Controller $controller, string $methodName): 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]); diff --git a/lib/private/DB/SchemaWrapper.php b/lib/private/DB/SchemaWrapper.php index 0d5b20405130f..19a0bc576f260 100644 --- a/lib/private/DB/SchemaWrapper.php +++ b/lib/private/DB/SchemaWrapper.php @@ -8,8 +8,11 @@ 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 @@ public function getTables() { 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]); + } + } + } } diff --git a/lib/private/Preview/Db/Preview.php b/lib/private/Preview/Db/Preview.php index 8d1c6d9f72aaa..d76435e23aaca 100644 --- a/lib/private/Preview/Db/Preview.php +++ b/lib/private/Preview/Db/Preview.php @@ -17,14 +17,16 @@ /** * 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 @@ * @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); diff --git a/lib/private/Preview/Db/PreviewMapper.php b/lib/private/Preview/Db/PreviewMapper.php index e6ca2e720f358..2a5342d7edbd6 100644 --- a/lib/private/Preview/Db/PreviewMapper.php +++ b/lib/private/Preview/Db/PreviewMapper.php @@ -15,6 +15,7 @@ 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 @@ public function insert(Entity $entity): Entity { 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 @@ protected function joinLocation(IQueryBuilder $qb): IQueryBuilder { )); } - 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 @@ public function getLocationId(string $bucket, string $objectStore): int { ->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; + } } } diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 8707c257e7b8a..8980e0b2d002a 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -23,6 +23,7 @@ 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 @@ public function __construct( private LoggerInterface $logger, private PreviewMapper $previewMapper, private StorageFactory $storageFactory, + private IGenerator $snowflakeGenerator, ) { } @@ -348,6 +350,7 @@ private function generateProviderPreview(File $file, int $width, int $height, bo 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 @@ private function generateProviderPreview(File $file, int $width, int $height, bo $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 @@ private function generatePreview( } $previewEntry = new Preview(); + $previewEntry->setId($this->snowflakeGenerator->nextId()); $previewEntry->setFileId($file->getId()); $previewEntry->setStorageId($file->getMountPoint()->getNumericStorageId()); $previewEntry->setWidth($width); @@ -514,7 +517,6 @@ private function generatePreview( $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 @@ private function generatePreview( * @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); } } diff --git a/lib/private/Preview/Storage/LocalPreviewStorage.php b/lib/private/Preview/Storage/LocalPreviewStorage.php index bd5e1a97818cd..3cf626280b2e6 100644 --- a/lib/private/Preview/Storage/LocalPreviewStorage.php +++ b/lib/private/Preview/Storage/LocalPreviewStorage.php @@ -22,6 +22,7 @@ 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 @@ public function __construct( 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 @@ public function scan(): int { $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()); diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index c230322b2e0f9..95a4f01b7a8e0 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -23,6 +23,7 @@ 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 @@ private function getGenerator(): Generator { $this->container->get(LoggerInterface::class), $this->container->get(PreviewMapper::class), $this->container->get(StorageFactory::class), + $this->container->get(IGenerator::class), ); } return $this->generator; diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php index 3094070af5f15..75dcd3e7766c2 100644 --- a/lib/public/AppFramework/Db/Entity.php +++ b/lib/public/AppFramework/Db/Entity.php @@ -19,10 +19,8 @@ * @psalm-consistent-constructor */ abstract class Entity { - /** - * @var int - */ - public $id; + /** @var int $id */ + public $id = null; private array $_updatedFields = []; /** @var array */ diff --git a/lib/public/DB/ISchemaWrapper.php b/lib/public/DB/ISchemaWrapper.php index dcf22b52d3d9e..8995fd5bfb1ee 100644 --- a/lib/public/DB/ISchemaWrapper.php +++ b/lib/public/DB/ISchemaWrapper.php @@ -90,4 +90,11 @@ public function getTableNamesWithoutPrefix(); * @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; } diff --git a/tests/lib/Preview/GeneratorTest.php b/tests/lib/Preview/GeneratorTest.php index ceaf483a6588c..074a3272dbf32 100644 --- a/tests/lib/Preview/GeneratorTest.php +++ b/tests/lib/Preview/GeneratorTest.php @@ -22,6 +22,7 @@ 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 @@ protected function setUp(): void { $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 @@ protected function setUp(): void { $this->logger, $this->previewMapper, $this->storageFactory, + $this->snowflakeGenerator, ); } diff --git a/tests/lib/Preview/MovePreviewJobTest.php b/tests/lib/Preview/MovePreviewJobTest.php index 69f730474e7bd..ee1bb0f9ab4af 100644 --- a/tests/lib/Preview/MovePreviewJobTest.php +++ b/tests/lib/Preview/MovePreviewJobTest.php @@ -24,6 +24,7 @@ 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 @@ public function testMigrationLegacyPath(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, + Server::get(IGenerator::class), Server::get(IAppDataFactory::class), ); $this->invokePrivate($job, 'run', [[]]); @@ -155,6 +157,7 @@ public function testMigrationPath(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, + Server::get(IGenerator::class), Server::get(IAppDataFactory::class) ); $this->invokePrivate($job, 'run', [[]]); @@ -195,6 +198,7 @@ public function testMigrationPathWithVersion(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, + Server::get(IGenerator::class), Server::get(IAppDataFactory::class) ); $this->invokePrivate($job, 'run', [[]]); diff --git a/tests/lib/Preview/PreviewMapperTest.php b/tests/lib/Preview/PreviewMapperTest.php index 0fc5420e23503..5ed1c6f8451ad 100644 --- a/tests/lib/Preview/PreviewMapperTest.php +++ b/tests/lib/Preview/PreviewMapperTest.php @@ -14,16 +14,28 @@ 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 @@ private function createPreviewForFileId(int $fileId, ?int $bucket = null): void $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); diff --git a/tests/lib/Preview/PreviewServiceTest.php b/tests/lib/Preview/PreviewServiceTest.php index 09f6a891402d9..a09b07380aabc 100644 --- a/tests/lib/Preview/PreviewServiceTest.php +++ b/tests/lib/Preview/PreviewServiceTest.php @@ -14,7 +14,7 @@ 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 @@ 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 @@ public function tearDown(): void { 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); diff --git a/version.php b/version.php index 7d52f0b5b1e5c..5633d19142f39 100644 --- a/version.php +++ b/version.php @@ -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';