diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php index 9121cb1f2a62e..42df72fe442eb 100644 --- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php @@ -44,7 +44,6 @@ use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; -use OCP\Snowflake\IGenerator; use OCP\Util; use Override; use Psr\Log\LoggerInterface; @@ -70,7 +69,6 @@ public function __construct( private readonly IFilenameValidator $filenameValidator, private readonly IProviderFactory $shareProviderFactory, private readonly SetupManager $setupManager, - private readonly IGenerator $snowflakeGenerator, private readonly ExternalShareMapper $externalShareMapper, ) { } @@ -145,7 +143,7 @@ public function shareReceived(ICloudFederationShare $share): string { } $externalShare = new ExternalShare(); - $externalShare->setId($this->snowflakeGenerator->nextId()); + $externalShare->setId(); $externalShare->setRemote($remote); $externalShare->setRemoteId($remoteId); $externalShare->setShareToken($token); @@ -177,9 +175,9 @@ public function shareReceived(ICloudFederationShare $share): string { ->setType('remote_share') ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($shareWith) - ->setObject('remote_share', $externalShare->getId(), $name); + ->setObject('remote_share', (string)$externalShare->getId(), $name); Server::get(IActivityManager::class)->publish($event); - $this->notifyAboutNewShare($shareWith, $externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); + $this->notifyAboutNewShare($shareWith, (string)$externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); // If auto-accept is enabled, accept the share if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { @@ -193,9 +191,9 @@ public function shareReceived(ICloudFederationShare $share): string { ->setType('remote_share') ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($user->getUID()) - ->setObject('remote_share', $externalShare->getId(), $name); + ->setObject('remote_share', (string)$externalShare->getId(), $name); Server::get(IActivityManager::class)->publish($event); - $this->notifyAboutNewShare($user->getUID(), $externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); + $this->notifyAboutNewShare($user->getUID(), (string)$externalShare->getId(), $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); // If auto-accept is enabled, accept the share if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { @@ -204,7 +202,7 @@ public function shareReceived(ICloudFederationShare $share): string { } } - return $externalShare->getId(); + return (string)$externalShare->getId(); } catch (\Exception $e) { $this->logger->error('Server can not add remote share.', [ 'app' => 'files_sharing', @@ -466,7 +464,7 @@ private function unshare(string $id, array $notification): array { $notification = $this->notificationManager->createNotification(); $notification->setApp('files_sharing') ->setUser($share->getUser()) - ->setObject('remote_share', $share->getId()); + ->setObject('remote_share', (string)$share->getId()); $this->notificationManager->markProcessed($notification); $event = $this->activityManager->generateEvent(); diff --git a/apps/files_sharing/lib/External/ExternalShare.php b/apps/files_sharing/lib/External/ExternalShare.php index db2c08f9dafa3..f6537211169e9 100644 --- a/apps/files_sharing/lib/External/ExternalShare.php +++ b/apps/files_sharing/lib/External/ExternalShare.php @@ -12,15 +12,13 @@ use OC\Files\Filesystem; use OCA\Files_Sharing\ResponseDefinitions; -use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\SnowflakeAwareEntity; use OCP\DB\Types; use OCP\IGroup; use OCP\IUser; use OCP\Share\IShare; /** - * @method string getId() - * @method void setId(string $id) * @method string getParent() * @method void setParent(string $parent) * @method int|null getShareType() @@ -46,7 +44,7 @@ * * @psalm-import-type Files_SharingRemoteShare from ResponseDefinitions */ -class ExternalShare extends Entity implements \JsonSerializable { +class ExternalShare extends SnowflakeAwareEntity implements \JsonSerializable { protected string $parent = '-1'; protected ?int $shareType = null; protected ?string $remote = null; @@ -96,7 +94,7 @@ public function setShareWith(IUser|IGroup $shareWith): void { public function jsonSerialize(): array { $parent = $this->getParent(); return [ - 'id' => $this->getId(), + 'id' => (string)$this->getId(), 'parent' => $parent, 'share_type' => $this->getShareType() ?? IShare::TYPE_USER, // unfortunately nullable on the DB level, but never null. 'remote' => $this->getRemote(), @@ -107,13 +105,6 @@ public function jsonSerialize(): array { 'user' => $this->getUser(), 'mountpoint' => $this->getMountpoint(), 'accepted' => $this->getAccepted(), - - // Added later on - 'file_id' => null, - 'mimetype' => null, - 'permissions' => null, - 'mtime' => null, - 'type' => null, ]; } diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php index 0958626b96d9d..9322348512b3e 100644 --- a/apps/files_sharing/lib/External/Manager.php +++ b/apps/files_sharing/lib/External/Manager.php @@ -34,7 +34,6 @@ use OCP\Notification\IManager; use OCP\OCS\IDiscoveryService; use OCP\Share\IShare; -use OCP\Snowflake\IGenerator; use Psr\Log\LoggerInterface; class Manager { @@ -57,7 +56,6 @@ public function __construct( private SetupManager $setupManager, private ICertificateManager $certificateManager, private ExternalShareMapper $externalShareMapper, - private IGenerator $snowflakeGenerator, ) { $this->user = $userSession->getUser(); } @@ -186,7 +184,7 @@ private function updateSubShare(ExternalShare $externalShare, IUser $user, ?stri $subShare = $this->externalShareMapper->getUserShare($externalShare, $user); } catch (DoesNotExistException) { $subShare = new ExternalShare(); - $subShare->setId($this->snowflakeGenerator->nextId()); + $subShare->setId(); $subShare->setRemote($externalShare->getRemote()); $subShare->setPassword($externalShare->getPassword()); $subShare->setName($externalShare->getName()); @@ -195,7 +193,7 @@ private function updateSubShare(ExternalShare $externalShare, IUser $user, ?stri $subShare->setMountpoint($mountPoint ?? $externalShare->getMountpoint()); $subShare->setAccepted($accepted); $subShare->setRemoteId($externalShare->getRemoteId()); - $subShare->setParent($externalShare->getId()); + $subShare->setParent((string)$externalShare->getId()); $subShare->setShareType($externalShare->getShareType()); $subShare->setShareToken($externalShare->getShareToken()); $this->externalShareMapper->insert($subShare); @@ -317,7 +315,7 @@ public function processNotification(ExternalShare $remoteShare, ?IUser $user = n $filter = $this->notificationManager->createNotification(); $filter->setApp('files_sharing') ->setUser($user->getUID()) - ->setObject('remote_share', $remoteShare->getId()); + ->setObject('remote_share', (string)$remoteShare->getId()); $this->notificationManager->markProcessed($filter); } diff --git a/apps/files_sharing/lib/ResponseDefinitions.php b/apps/files_sharing/lib/ResponseDefinitions.php index 58277577eabdd..72a7b4efca40c 100644 --- a/apps/files_sharing/lib/ResponseDefinitions.php +++ b/apps/files_sharing/lib/ResponseDefinitions.php @@ -82,21 +82,21 @@ * * @psalm-type Files_SharingRemoteShare = array{ * accepted: int, - * file_id: int|null, * id: string, - * mimetype: string|null, * mountpoint: string, - * mtime: int|null, * name: string, * owner: string, * parent: string|null, - * permissions: int|null, * remote: string, * remote_id: string, * share_token: string, * share_type: int, - * type: string|null, * user: string, + * file_id?: int, + * mimetype?: string, + * permissions?: int, + * mtime?: int, + * type?: string, * } * * @psalm-type Files_SharingSharee = array{ diff --git a/apps/files_sharing/tests/Command/CleanupRemoteStoragesTest.php b/apps/files_sharing/tests/Command/CleanupRemoteStoragesTest.php index c105157b1a985..088bfd1f46721 100644 --- a/apps/files_sharing/tests/Command/CleanupRemoteStoragesTest.php +++ b/apps/files_sharing/tests/Command/CleanupRemoteStoragesTest.php @@ -14,7 +14,6 @@ use OCP\Federation\ICloudIdManager; use OCP\IDBConnection; use OCP\Server; -use OCP\Snowflake\IGenerator; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Console\Input\InputInterface; @@ -64,7 +63,7 @@ protected function setUp(): void { if (isset($storage['share_token'])) { $externalShare = new ExternalShare(); - $externalShare->setId(Server::get(IGenerator::class)->nextId()); + $externalShare->setId(); $externalShare->setShareToken($storage['share_token']); $externalShare->setRemote($storage['remote']); $externalShare->setName('irrelevant'); diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php index 64afb91b0809c..942c1769601bd 100644 --- a/apps/files_sharing/tests/External/ManagerTest.php +++ b/apps/files_sharing/tests/External/ManagerTest.php @@ -40,7 +40,6 @@ use OCP\OCS\IDiscoveryService; use OCP\Server; use OCP\Share\IShare; -use OCP\Snowflake\IGenerator; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Test\Traits\UserTrait; @@ -170,7 +169,6 @@ private function createManagerForUser(IUser $user): Manager&MockObject { $this->setupManager, $this->certificateManager, $this->externalShareMapper, - Server::get(IGenerator::class), ] )->onlyMethods(['tryOCMEndPoint'])->getMock(); } @@ -190,7 +188,7 @@ private function clearMounts(): void { public function testAddUserShare(): void { $userShare = new ExternalShare(); - $userShare->setId(Server::get(IGenerator::class)->nextId()); + $userShare->setId(); $userShare->setRemote('http://localhost'); $userShare->setShareToken('token1'); $userShare->setPassword(''); @@ -205,7 +203,7 @@ public function testAddUserShare(): void { public function testAddGroupShare(): void { $groupShare = new ExternalShare(); - $groupShare->setId(Server::get(IGenerator::class)->nextId()); + $groupShare->setId(); $groupShare->setRemote('http://localhost'); $groupShare->setOwner('foobar'); $groupShare->setShareType(IShare::TYPE_GROUP); @@ -237,10 +235,10 @@ public function doTestAddShare(ExternalShare $shareData1, IUser|IGroup $userOrGr $shareData2 = $shareData1->clone(); $shareData2->setShareToken('token2'); - $shareData2->setId(\OCP\Server::get(IGenerator::class)->nextId()); + $shareData2->setId(); $shareData3 = $shareData1->clone(); $shareData3->setShareToken('token3'); - $shareData3->setId(\OCP\Server::get(IGenerator::class)->nextId()); + $shareData3->setId(); $this->setupMounts(); $this->assertNotMount('SharedFolder'); @@ -440,7 +438,7 @@ private function createTestUserShare(string $userId = 'user1'): ExternalShare { $user = $this->createMock(IUser::class); $user->expects($this->any())->method('getUID')->willReturn($userId); $share = new ExternalShare(); - $share->setId(Server::get(IGenerator::class)->nextId()); + $share->setId(); $share->setRemote('http://localhost'); $share->setShareToken('token1'); $share->setPassword(''); @@ -460,7 +458,7 @@ private function createTestUserShare(string $userId = 'user1'): ExternalShare { */ private function createTestGroupShare(string $groupId = 'group1'): array { $share = new ExternalShare(); - $share->setId(Server::get(IGenerator::class)->nextId()); + $share->setId(); $share->setRemote('http://localhost'); $share->setShareToken('token1'); $share->setPassword(''); @@ -646,7 +644,7 @@ public function testDeleteUserShares(): void { // user 2 shares $manager2 = $this->createManagerForUser($user2); $share = new ExternalShare(); - $share->setId(Server::get(IGenerator::class)->nextId()); + $share->setId(); $share->setRemote('http://localhost'); $share->setShareToken('token1'); $share->setPassword(''); @@ -696,7 +694,7 @@ public function testDeleteGroupShares(): void { $manager2 = $this->createManagerForUser($user); $share = new ExternalShare(); - $share->setId(Server::get(IGenerator::class)->nextId()); + $share->setId(); $share->setRemote('http://localhost'); $share->setShareToken('token1'); $share->setPassword(''); diff --git a/apps/files_versions/lib/Db/VersionEntity.php b/apps/files_versions/lib/Db/VersionEntity.php index 10f1dc8cbba4a..20102e6760235 100644 --- a/apps/files_versions/lib/Db/VersionEntity.php +++ b/apps/files_versions/lib/Db/VersionEntity.php @@ -44,7 +44,7 @@ public function __construct() { public function jsonSerialize(): array { return [ - 'id' => $this->id, + 'id' => $this->getId(), 'file_id' => $this->fileId, 'timestamp' => $this->timestamp, 'size' => $this->size, diff --git a/core/BackgroundJobs/MovePreviewJob.php b/core/BackgroundJobs/MovePreviewJob.php index 1442b22b32776..ce847a7a96fb2 100644 --- a/core/BackgroundJobs/MovePreviewJob.php +++ b/core/BackgroundJobs/MovePreviewJob.php @@ -26,7 +26,6 @@ use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; -use OCP\Snowflake\IGenerator; use Override; use Psr\Log\LoggerInterface; @@ -45,7 +44,6 @@ public function __construct( private readonly IMimeTypeDetector $mimeTypeDetector, private readonly IMimeTypeLoader $mimeTypeLoader, private readonly LoggerInterface $logger, - private readonly IGenerator $generator, IAppDataFactory $appDataFactory, ) { parent::__construct($time); @@ -138,7 +136,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()); + $preview->setId(); if (!$preview) { $this->logger->error('Unable to import old preview at path.'); continue; @@ -172,6 +170,7 @@ private function processPreviews(int $fileId, bool $flatPath): void { $preview->setStorageId($result[0]['storage']); $preview->setEtag($result[0]['etag']); $preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype'])); + $preview->setId(); try { $preview = $this->previewMapper->insert($preview); } catch (Exception) { diff --git a/core/Command/SnowflakeDecodeId.php b/core/Command/SnowflakeDecodeId.php index 3ce7c038ff76d..e9993077aaea4 100644 --- a/core/Command/SnowflakeDecodeId.php +++ b/core/Command/SnowflakeDecodeId.php @@ -8,7 +8,7 @@ */ namespace OC\Core\Command; -use OCP\Snowflake\IDecoder; +use OCP\Snowflake\ISnowflakeDecoder; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -16,7 +16,7 @@ class SnowflakeDecodeId extends Base { public function __construct( - private readonly IDecoder $decoder, + private readonly ISnowflakeDecoder $decoder, ) { parent::__construct(); } @@ -36,13 +36,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $rows = [ ['Snowflake ID', $snowflakeId], - ['Seconds', $data['seconds']], - ['Milliseconds', $data['milliseconds']], - ['Created from CLI', $data['isCli'] ? 'yes' : 'no'], - ['Server ID', $data['serverId']], - ['Sequence ID', $data['sequenceId']], - ['Creation timestamp', $data['createdAt']->format('U.v')], - ['Creation date', $data['createdAt']->format('Y-m-d H:i:s.v')], + ['Seconds', $data->getSeconds()], + ['Milliseconds', $data->getMilliseconds()], + ['Created from CLI', $data->isCli() ? 'yes' : 'no'], + ['Server ID', $data->getServerId()], + ['Sequence ID', $data->getSequenceId()], + ['Creation timestamp', $data->getCreatedAt()->format('U.v')], + ['Creation date', $data->getCreatedAt()->format('Y-m-d H:i:s.v')], ]; $table = new Table($output); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 0b6dd29f2040b..46dd71f5a1e59 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -78,6 +78,7 @@ 'OCP\\AppFramework\\Db\\IMapperException' => $baseDir . '/lib/public/AppFramework/Db/IMapperException.php', 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => $baseDir . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => $baseDir . '/lib/public/AppFramework/Db/QBMapper.php', + 'OCP\\AppFramework\\Db\\SnowflakeAwareEntity' => $baseDir . '/lib/public/AppFramework/Db/SnowflakeAwareEntity.php', 'OCP\\AppFramework\\Db\\TTransactional' => $baseDir . '/lib/public/AppFramework/Db/TTransactional.php', 'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php', @@ -830,8 +831,9 @@ 'OCP\\Share_Backend' => $baseDir . '/lib/public/Share_Backend.php', 'OCP\\Share_Backend_Collection' => $baseDir . '/lib/public/Share_Backend_Collection.php', 'OCP\\Share_Backend_File_Dependent' => $baseDir . '/lib/public/Share_Backend_File_Dependent.php', - 'OCP\\Snowflake\\IDecoder' => $baseDir . '/lib/public/Snowflake/IDecoder.php', - 'OCP\\Snowflake\\IGenerator' => $baseDir . '/lib/public/Snowflake/IGenerator.php', + 'OCP\\Snowflake\\ISnowflakeDecoder' => $baseDir . '/lib/public/Snowflake/ISnowflakeDecoder.php', + 'OCP\\Snowflake\\ISnowflakeGenerator' => $baseDir . '/lib/public/Snowflake/ISnowflakeGenerator.php', + 'OCP\\Snowflake\\Snowflake' => $baseDir . '/lib/public/Snowflake/Snowflake.php', 'OCP\\SpeechToText\\Events\\AbstractTranscriptionEvent' => $baseDir . '/lib/public/SpeechToText/Events/AbstractTranscriptionEvent.php', 'OCP\\SpeechToText\\Events\\TranscriptionFailedEvent' => $baseDir . '/lib/public/SpeechToText/Events/TranscriptionFailedEvent.php', 'OCP\\SpeechToText\\Events\\TranscriptionSuccessfulEvent' => $baseDir . '/lib/public/SpeechToText/Events/TranscriptionSuccessfulEvent.php', @@ -2125,10 +2127,10 @@ 'OC\\Share\\Helper' => $baseDir . '/lib/private/Share/Helper.php', 'OC\\Share\\Share' => $baseDir . '/lib/private/Share/Share.php', 'OC\\Snowflake\\APCuSequence' => $baseDir . '/lib/private/Snowflake/APCuSequence.php', - 'OC\\Snowflake\\Decoder' => $baseDir . '/lib/private/Snowflake/Decoder.php', 'OC\\Snowflake\\FileSequence' => $baseDir . '/lib/private/Snowflake/FileSequence.php', - 'OC\\Snowflake\\Generator' => $baseDir . '/lib/private/Snowflake/Generator.php', 'OC\\Snowflake\\ISequence' => $baseDir . '/lib/private/Snowflake/ISequence.php', + 'OC\\Snowflake\\SnowflakeDecoder' => $baseDir . '/lib/private/Snowflake/SnowflakeDecoder.php', + 'OC\\Snowflake\\SnowflakeGenerator' => $baseDir . '/lib/private/Snowflake/SnowflakeGenerator.php', 'OC\\SpeechToText\\SpeechToTextManager' => $baseDir . '/lib/private/SpeechToText/SpeechToTextManager.php', 'OC\\SpeechToText\\TranscriptionJob' => $baseDir . '/lib/private/SpeechToText/TranscriptionJob.php', 'OC\\StreamImage' => $baseDir . '/lib/private/StreamImage.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9c34d2cd4debd..361d139ff4ccc 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -119,6 +119,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\AppFramework\\Db\\IMapperException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/IMapperException.php', 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/QBMapper.php', + 'OCP\\AppFramework\\Db\\SnowflakeAwareEntity' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/SnowflakeAwareEntity.php', 'OCP\\AppFramework\\Db\\TTransactional' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/TTransactional.php', 'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php', 'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php', @@ -871,8 +872,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Share_Backend' => __DIR__ . '/../../..' . '/lib/public/Share_Backend.php', 'OCP\\Share_Backend_Collection' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_Collection.php', 'OCP\\Share_Backend_File_Dependent' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_File_Dependent.php', - 'OCP\\Snowflake\\IDecoder' => __DIR__ . '/../../..' . '/lib/public/Snowflake/IDecoder.php', - 'OCP\\Snowflake\\IGenerator' => __DIR__ . '/../../..' . '/lib/public/Snowflake/IGenerator.php', + 'OCP\\Snowflake\\ISnowflakeDecoder' => __DIR__ . '/../../..' . '/lib/public/Snowflake/ISnowflakeDecoder.php', + 'OCP\\Snowflake\\ISnowflakeGenerator' => __DIR__ . '/../../..' . '/lib/public/Snowflake/ISnowflakeGenerator.php', + 'OCP\\Snowflake\\Snowflake' => __DIR__ . '/../../..' . '/lib/public/Snowflake/Snowflake.php', 'OCP\\SpeechToText\\Events\\AbstractTranscriptionEvent' => __DIR__ . '/../../..' . '/lib/public/SpeechToText/Events/AbstractTranscriptionEvent.php', 'OCP\\SpeechToText\\Events\\TranscriptionFailedEvent' => __DIR__ . '/../../..' . '/lib/public/SpeechToText/Events/TranscriptionFailedEvent.php', 'OCP\\SpeechToText\\Events\\TranscriptionSuccessfulEvent' => __DIR__ . '/../../..' . '/lib/public/SpeechToText/Events/TranscriptionSuccessfulEvent.php', @@ -2166,10 +2168,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Share\\Helper' => __DIR__ . '/../../..' . '/lib/private/Share/Helper.php', 'OC\\Share\\Share' => __DIR__ . '/../../..' . '/lib/private/Share/Share.php', 'OC\\Snowflake\\APCuSequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/APCuSequence.php', - 'OC\\Snowflake\\Decoder' => __DIR__ . '/../../..' . '/lib/private/Snowflake/Decoder.php', 'OC\\Snowflake\\FileSequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/FileSequence.php', - 'OC\\Snowflake\\Generator' => __DIR__ . '/../../..' . '/lib/private/Snowflake/Generator.php', 'OC\\Snowflake\\ISequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/ISequence.php', + 'OC\\Snowflake\\SnowflakeDecoder' => __DIR__ . '/../../..' . '/lib/private/Snowflake/SnowflakeDecoder.php', + 'OC\\Snowflake\\SnowflakeGenerator' => __DIR__ . '/../../..' . '/lib/private/Snowflake/SnowflakeGenerator.php', 'OC\\SpeechToText\\SpeechToTextManager' => __DIR__ . '/../../..' . '/lib/private/SpeechToText/SpeechToTextManager.php', 'OC\\SpeechToText\\TranscriptionJob' => __DIR__ . '/../../..' . '/lib/private/SpeechToText/TranscriptionJob.php', 'OC\\StreamImage' => __DIR__ . '/../../..' . '/lib/private/StreamImage.php', diff --git a/lib/private/Authentication/Token/PublicKeyToken.php b/lib/private/Authentication/Token/PublicKeyToken.php index be427ab48392a..727496bff7679 100644 --- a/lib/private/Authentication/Token/PublicKeyToken.php +++ b/lib/private/Authentication/Token/PublicKeyToken.php @@ -101,10 +101,6 @@ public function __construct() { $this->addType('passwordInvalid', Types::BOOLEAN); } - public function getId(): int { - return $this->id; - } - public function getUID(): string { return $this->uid; } @@ -127,7 +123,7 @@ public function getPassword(): ?string { public function jsonSerialize(): array { return [ - 'id' => $this->id, + 'id' => $this->getId(), 'name' => $this->name, 'lastActivity' => $this->lastActivity, 'type' => $this->type, diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 846c75626384f..4185fde4c2754 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -16,7 +16,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use Override; use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; @@ -34,7 +34,7 @@ public function __construct( protected readonly IConfig $config, protected readonly ITimeFactory $timeFactory, protected readonly LoggerInterface $logger, - protected readonly IGenerator $generator, + protected readonly ISnowflakeGenerator $snowflakeGenerator, ) { } @@ -55,7 +55,7 @@ public function add(IJob|string $job, mixed $argument = null, ?int $firstCheck = if (!$this->has($job, $argument)) { $query->insert('jobs') ->values([ - 'id' => $query->createNamedParameter($this->generator->nextId()), + 'id' => $query->createNamedParameter($this->snowflakeGenerator->nextId()), 'class' => $query->createNamedParameter($class), 'argument' => $query->createNamedParameter($argumentJson), 'argument_hash' => $query->createNamedParameter(hash('sha256', $argumentJson)), diff --git a/lib/private/Preview/Db/Preview.php b/lib/private/Preview/Db/Preview.php index d76435e23aaca..fe2509158e8b7 100644 --- a/lib/private/Preview/Db/Preview.php +++ b/lib/private/Preview/Db/Preview.php @@ -11,14 +11,13 @@ namespace OC\Preview\Db; use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\SnowflakeAwareEntity; use OCP\DB\Types; 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. @@ -55,7 +54,7 @@ * * @see PreviewMapper */ -class Preview extends Entity { +class Preview extends SnowflakeAwareEntity { protected ?int $fileId = null; protected ?int $oldFileId = null; protected ?int $storageId = null; diff --git a/lib/private/Preview/Db/PreviewMapper.php b/lib/private/Preview/Db/PreviewMapper.php index 2fe5c5afebfa4..cf442e51691c4 100644 --- a/lib/private/Preview/Db/PreviewMapper.php +++ b/lib/private/Preview/Db/PreviewMapper.php @@ -15,7 +15,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\IMimeTypeLoader; use OCP\IDBConnection; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use Override; /** @@ -30,7 +30,7 @@ class PreviewMapper extends QBMapper { public function __construct( IDBConnection $db, private readonly IMimeTypeLoader $mimeTypeLoader, - private readonly IGenerator $snowflake, + private readonly ISnowflakeGenerator $snowflake, ) { parent::__construct($db, self::TABLE_NAME, Preview::class); } @@ -52,15 +52,14 @@ 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' => $qb->createNamedParameter($id), + 'id' => $qb->createNamedParameter($preview->getId()), 'version' => $qb->createNamedParameter($preview->getVersion(), IQueryBuilder::PARAM_STR), 'file_id' => $qb->createNamedParameter($preview->getFileId()), ]) ->executeStatement(); - $entity->setVersionId($id); + $entity->setVersionId((string)$preview->getId()); } return parent::insert($preview); } diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 8980e0b2d002a..f09791853392e 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -23,7 +23,6 @@ use OCP\IStreamImage; use OCP\Preview\BeforePreviewFetchedEvent; use OCP\Preview\IVersionedPreviewFile; -use OCP\Snowflake\IGenerator; use Psr\Log\LoggerInterface; class Generator { @@ -38,7 +37,6 @@ public function __construct( private LoggerInterface $logger, private PreviewMapper $previewMapper, private StorageFactory $storageFactory, - private IGenerator $snowflakeGenerator, ) { } @@ -350,7 +348,7 @@ private function generateProviderPreview(File $file, int $width, int $height, bo try { $previewEntry = new Preview(); - $previewEntry->setId($this->snowflakeGenerator->nextId()); + $previewEntry->setId(); $previewEntry->setFileId($file->getId()); $previewEntry->setStorageId($file->getMountPoint()->getNumericStorageId()); $previewEntry->setSourceMimeType($file->getMimeType()); @@ -504,7 +502,7 @@ private function generatePreview( } $previewEntry = new Preview(); - $previewEntry->setId($this->snowflakeGenerator->nextId()); + $previewEntry->setId(); $previewEntry->setFileId($file->getId()); $previewEntry->setStorageId($file->getMountPoint()->getNumericStorageId()); $previewEntry->setWidth($width); @@ -545,7 +543,7 @@ public function savePreview(Preview $previewEntry, IImage $preview): Preview { throw new \RuntimeException('Unable to write preview file'); } $previewEntry->setSize($size); - + $previewEntry->setId(); return $this->previewMapper->insert($previewEntry); } } diff --git a/lib/private/Preview/Storage/LocalPreviewStorage.php b/lib/private/Preview/Storage/LocalPreviewStorage.php index 3cf626280b2e6..5d4c7a630dd62 100644 --- a/lib/private/Preview/Storage/LocalPreviewStorage.php +++ b/lib/private/Preview/Storage/LocalPreviewStorage.php @@ -22,7 +22,7 @@ use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use Override; use Psr\Log\LoggerInterface; use RecursiveDirectoryIterator; @@ -39,7 +39,7 @@ public function __construct( private readonly IDBConnection $connection, private readonly IMimeTypeDetector $mimeTypeDetector, private readonly LoggerInterface $logger, - private readonly IGenerator $generator, + private readonly ISnowflakeGenerator $generator, ) { $this->instanceId = $this->config->getSystemValueString('instanceid'); $this->rootFolder = $this->config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data'); @@ -120,7 +120,7 @@ public function scan(): int { $this->logger->error('Unable to parse preview information for ' . $file->getRealPath()); continue; } - $preview->setId($this->generator->nextId()); + $preview->setId(); try { $preview->setSize($file->getSize()); $preview->setMtime($file->getMtime()); @@ -154,7 +154,7 @@ public function scan(): int { $preview->setStorageId($result[0]['storage']); $preview->setEtag($result[0]['etag']); $preview->setSourceMimetype($result[0]['mimetype']); - + $preview->setId(); // try to insert, if that fails the preview is already in the DB $this->previewMapper->insert($preview); diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index 0e764100ad1c4..9684ad240e174 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -23,7 +23,6 @@ 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; @@ -142,7 +141,6 @@ 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/private/Server.php b/lib/private/Server.php index 51682ec36302d..a4cf03cdc4fcf 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -117,10 +117,10 @@ use OC\Share20\ProviderFactory; use OC\Share20\ShareHelper; use OC\Snowflake\APCuSequence; -use OC\Snowflake\Decoder; use OC\Snowflake\FileSequence; -use OC\Snowflake\Generator; use OC\Snowflake\ISequence; +use OC\Snowflake\SnowflakeDecoder; +use OC\Snowflake\SnowflakeGenerator; use OC\SpeechToText\SpeechToTextManager; use OC\SystemTag\ManagerFactory as SystemTagManagerFactory; use OC\Talk\Broker; @@ -179,6 +179,7 @@ use OCP\ICache; use OCP\ICacheFactory; use OCP\ICertificateManager; +use OCP\IConfig; use OCP\IDateTimeFormatter; use OCP\IDateTimeZone; use OCP\IDBConnection; @@ -230,8 +231,8 @@ use OCP\SetupCheck\ISetupCheckManager; use OCP\Share\IProviderFactory; use OCP\Share\IShareHelper; -use OCP\Snowflake\IDecoder; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeDecoder; +use OCP\Snowflake\ISnowflakeGenerator; use OCP\SpeechToText\ISpeechToTextManager; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; @@ -1266,7 +1267,7 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(ISignatureManager::class, SignatureManager::class); - $this->registerAlias(IGenerator::class, Generator::class); + $this->registerAlias(ISnowflakeGenerator::class, SnowflakeGenerator::class); $this->registerService(ISequence::class, function (ContainerInterface $c): ISequence { if (PHP_SAPI !== 'cli') { $sequence = $c->get(APCuSequence::class); @@ -1277,7 +1278,7 @@ public function __construct($webRoot, \OC\Config $config) { return $c->get(FileSequence::class); }, false); - $this->registerAlias(IDecoder::class, Decoder::class); + $this->registerAlias(ISnowflakeDecoder::class, SnowflakeDecoder::class); $this->connectDispatcher(); } diff --git a/lib/private/Settings/AuthorizedGroup.php b/lib/private/Settings/AuthorizedGroup.php index 0171e020171c3..a6039a66530fe 100644 --- a/lib/private/Settings/AuthorizedGroup.php +++ b/lib/private/Settings/AuthorizedGroup.php @@ -23,7 +23,7 @@ class AuthorizedGroup extends Entity implements \JsonSerializable { public function jsonSerialize(): array { return [ - 'id' => $this->id, + 'id' => $this->getId(), 'group_id' => $this->groupId, 'class' => $this->class ]; diff --git a/lib/private/Snowflake/APCuSequence.php b/lib/private/Snowflake/APCuSequence.php index e5aad4636efe5..82cd1c46e8bbe 100644 --- a/lib/private/Snowflake/APCuSequence.php +++ b/lib/private/Snowflake/APCuSequence.php @@ -26,11 +26,7 @@ public function nextId(int $serverId, int $seconds, int $milliseconds): int|fals } $key = 'seq:' . $seconds . ':' . $milliseconds; - $sequenceId = apcu_inc($key, success: $success, ttl: 1); - if ($success === true) { - return $sequenceId; - } - - throw new \Exception('Failed to generate SnowflakeId with APCu'); + $sequenceId = apcu_inc($key, ttl: 1); + return $sequenceId === false ? throw new \Exception('Failed to generate SnowflakeId with APCu') : $sequenceId; } } diff --git a/lib/private/Snowflake/Decoder.php b/lib/private/Snowflake/SnowflakeDecoder.php similarity index 82% rename from lib/private/Snowflake/Decoder.php rename to lib/private/Snowflake/SnowflakeDecoder.php index e4ec72e50d2c0..565729cf55d01 100644 --- a/lib/private/Snowflake/Decoder.php +++ b/lib/private/Snowflake/SnowflakeDecoder.php @@ -9,8 +9,9 @@ namespace OC\Snowflake; -use OCP\Snowflake\IDecoder; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeDecoder; +use OCP\Snowflake\ISnowflakeGenerator; +use OCP\Snowflake\Snowflake; use Override; /** @@ -20,9 +21,9 @@ * * @since 33.0.0 */ -final class Decoder implements IDecoder { +final class SnowflakeDecoder implements ISnowflakeDecoder { #[Override] - public function decode(string $snowflakeId): array { + public function decode(string $snowflakeId): Snowflake { if (!ctype_digit($snowflakeId)) { throw new \Exception('Invalid Snowflake ID: ' . $snowflakeId); } @@ -35,12 +36,19 @@ public function decode(string $snowflakeId): array { $data['createdAt'] = new \DateTimeImmutable( sprintf( '@%d.%03d', - $data['seconds'] + IGenerator::TS_OFFSET + intdiv($data['milliseconds'], 1000), + $data['seconds'] + ISnowflakeGenerator::TS_OFFSET + intdiv($data['milliseconds'], 1000), $data['milliseconds'] % 1000, ) ); - return $data; + return new Snowflake( + $data['serverId'], + $data['sequenceId'], + $data['isCli'], + $data['seconds'], + $data['milliseconds'], + $data['createdAt'], + ); } private function decode64bits(int $snowflakeId): array { @@ -51,11 +59,11 @@ private function decode64bits(int $snowflakeId): array { $milliseconds = $secondHalf >> 22; return [ - 'seconds' => $seconds, - 'milliseconds' => $milliseconds, 'serverId' => ($secondHalf >> 13) & 0x1FF, 'sequenceId' => $secondHalf & 0xFFF, 'isCli' => (bool)(($secondHalf >> 12) & 0x1), + 'seconds' => $seconds, + 'milliseconds' => $milliseconds, ]; } @@ -88,12 +96,12 @@ private function convertBase16(string $decimal): string { $hex = ''; $digits = '0123456789ABCDEF'; - while (strlen($decimal) > 0 && $decimal !== '0') { + while ($decimal !== '' && $decimal !== '0') { $remainder = 0; $newDecimal = ''; // Perform division by 16 manually for arbitrary precision - for ($i = 0; $i < strlen($decimal); $i++) { + for ($i = 0, $iMax = strlen($decimal); $i < $iMax; $i++) { $digit = (int)$decimal[$i]; $current = $remainder * 10 + $digit; @@ -104,7 +112,7 @@ private function convertBase16(string $decimal): string { } else { $remainder = $current; // Only add quotient digit if we already have some digits in result - if (strlen($newDecimal) > 0) { + if ($newDecimal !== '') { $newDecimal .= '0'; } } diff --git a/lib/private/Snowflake/Generator.php b/lib/private/Snowflake/SnowflakeGenerator.php similarity index 92% rename from lib/private/Snowflake/Generator.php rename to lib/private/Snowflake/SnowflakeGenerator.php index 46e6e43b326e4..fcadc2a2a8ca1 100644 --- a/lib/private/Snowflake/Generator.php +++ b/lib/private/Snowflake/SnowflakeGenerator.php @@ -11,7 +11,7 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use Override; /** @@ -21,11 +21,11 @@ * * @since 33.0.0 */ -final class Generator implements IGenerator { +final readonly class SnowflakeGenerator implements ISnowflakeGenerator { public function __construct( - private readonly ITimeFactory $timeFactory, - private readonly IConfig $config, - private readonly ISequence $sequenceGenerator, + private ITimeFactory $timeFactory, + private IConfig $config, + private ISequence $sequenceGenerator, ) { } diff --git a/lib/private/Tags.php b/lib/private/Tags.php index 309d373934896..fb59fb670a6f9 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -290,7 +290,7 @@ public function add(string $name) { return false; } $this->logger->debug(__METHOD__ . ' Added an tag with ' . $tag->getId(), ['app' => 'core']); - return $tag->getId(); + return $tag->getId() ?? false; } /** diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php index 75dcd3e7766c2..764b7d92237a0 100644 --- a/lib/public/AppFramework/Db/Entity.php +++ b/lib/public/AppFramework/Db/Entity.php @@ -13,19 +13,23 @@ use function substr; /** - * @method int getId() - * @method void setId(int $id) * @since 7.0.0 * @psalm-consistent-constructor */ abstract class Entity { - /** @var int $id */ - public $id = null; - + private ?int $id = null; private array $_updatedFields = []; - /** @var array */ + /** @psalm-param $_fieldTypes array */ private array $_fieldTypes = ['id' => 'integer']; + public function setId($id): void { + $this->id = $id; + } + + public function getId(): ?int { + return $this->id; + } + /** * Simple alternative constructor for building entities from a request * @param array $params the array which was obtained via $this->params('key') @@ -64,7 +68,7 @@ public static function fromRow(array $row): static { /** - * @return array with attribute and type + * @return array with attribute and type * @since 7.0.0 */ public function getFieldTypes(): array { @@ -266,8 +270,8 @@ public function getUpdatedFields(): array { * that value once its being returned from the database * * @param string $fieldName the name of the attribute - * @param \OCP\DB\Types::* $type the type which will be used to match a cast - * @since 31.0.0 Parameter $type is now restricted to {@see \OCP\DB\Types} constants. The formerly accidentally supported types 'int'|'bool'|'double' are mapped to Types::INTEGER|Types::BOOLEAN|Types::FLOAT accordingly. + * @param Types::* $type the type which will be used to match a cast + * @since 31.0.0 Parameter $type is now restricted to {@see Types} constants. The formerly accidentally supported types 'int'|'bool'|'double' are mapped to Types::INTEGER|Types::BOOLEAN|Types::FLOAT accordingly. * @since 7.0.0 */ protected function addType(string $fieldName, string $type): void { diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php index d80bb5aec8b97..9722b0c94ad21 100644 --- a/lib/public/AppFramework/Db/QBMapper.php +++ b/lib/public/AppFramework/Db/QBMapper.php @@ -112,13 +112,17 @@ public function insert(Entity $entity): Entity { $qb->setValue($column, $qb->createNamedParameter($value, $type)); } - $qb->executeStatement(); - - if ($entity->id === null) { + if ($entity instanceof SnowflakeAwareEntity) { + /** @psalm-suppress DocblockTypeContradiction */ + $entity->setId(); + $qb->executeStatement(); + } elseif ($entity->getId() === null) { + $qb->executeStatement(); // When autoincrement is used id is always an int $entity->setId($qb->getLastInsertId()); + } else { + $qb->executeStatement(); } - return $entity; } diff --git a/lib/public/AppFramework/Db/SnowflakeAwareEntity.php b/lib/public/AppFramework/Db/SnowflakeAwareEntity.php new file mode 100644 index 0000000000000..a74fbee1e923a --- /dev/null +++ b/lib/public/AppFramework/Db/SnowflakeAwareEntity.php @@ -0,0 +1,56 @@ + */ + private array $_fieldTypes = ['id' => Types::STRING]; + + /** + * Automatically creates a snowflake ID + */ + #[\Override] + public function setId($id = null): void { + if ($this->id === null) { + $this->id = Server::get(ISnowflakeGenerator::class)->nextId(); + $this->markFieldUpdated('id'); + } + } + + public function getCreatedAt(): ?\DateTimeImmutable { + return $this->getSnowflake()?->getCreatedAt(); + } + + public function getSnowflake(): ?Snowflake { + if ($this->id === null) { + return null; + } + + if ($this->snowflake === null) { + $this->snowflake = Server::get(ISnowflakeDecoder::class)->decode($this->id); + } + + return $this->snowflake; + } +} diff --git a/lib/public/Authentication/Token/IToken.php b/lib/public/Authentication/Token/IToken.php index 8c04728092446..e3283ea980350 100644 --- a/lib/public/Authentication/Token/IToken.php +++ b/lib/public/Authentication/Token/IToken.php @@ -47,7 +47,7 @@ interface IToken extends JsonSerializable { * Get the token ID * @since 28.0.0 */ - public function getId(): int; + public function getId(): ?int; /** * Get the user UID diff --git a/lib/public/Snowflake/IDecoder.php b/lib/public/Snowflake/ISnowflakeDecoder.php similarity index 70% rename from lib/public/Snowflake/IDecoder.php rename to lib/public/Snowflake/ISnowflakeDecoder.php index f75019981b10e..020dd1f65d4ac 100644 --- a/lib/public/Snowflake/IDecoder.php +++ b/lib/public/Snowflake/ISnowflakeDecoder.php @@ -14,11 +14,11 @@ /** * Nextcloud Snowflake ID decoder * - * @see \OCP\Snowflake\IGenerator for format + * @see \OCP\Snowflake\ISnowflakeGenerator for format * @since 33.0.0 */ #[Consumable(since: '33.0.0')] -interface IDecoder { +interface ISnowflakeDecoder { /** * Decode information contained into Snowflake ID * @@ -28,8 +28,8 @@ interface IDecoder { * - createdAt: timestamp at which ID was generated * - isCli: if ID was generated using CLI or not * - * @return array{createdAt: \DateTimeImmutable, serverId: int<0,1023>, sequenceId: int<0,4095>, isCli: bool, seconds: positive-int, milliseconds: int<0,999>} + * @return Snowflake * @since 33.0 */ - public function decode(string $snowflakeId): array; + public function decode(string $snowflakeId): Snowflake; } diff --git a/lib/public/Snowflake/IGenerator.php b/lib/public/Snowflake/ISnowflakeGenerator.php similarity index 97% rename from lib/public/Snowflake/IGenerator.php rename to lib/public/Snowflake/ISnowflakeGenerator.php index 2a465861f8dbd..8b7e030dbf311 100644 --- a/lib/public/Snowflake/IGenerator.php +++ b/lib/public/Snowflake/ISnowflakeGenerator.php @@ -25,7 +25,7 @@ * @since 33.0.0 */ #[Consumable(since: '33.0.0')] -interface IGenerator { +interface ISnowflakeGenerator { /** * Offset applied on timestamps to keep it short diff --git a/lib/public/Snowflake/Snowflake.php b/lib/public/Snowflake/Snowflake.php new file mode 100644 index 0000000000000..9fce0a5aea2f7 --- /dev/null +++ b/lib/public/Snowflake/Snowflake.php @@ -0,0 +1,72 @@ + $serverId + * @psalm-param int<0,4095> $sequenceId + * @psalm-param non-negative-int $seconds + * @psalm-param int<0,999> $milliseconds + */ + public function __construct( + private int $serverId, + private int $sequenceId, + private bool $isCli, + private int $seconds, + private int $milliseconds, + private \DateTimeImmutable $createdAt, + ) { + } + + /** + * @psalm-return int<0,1023> + */ + public function getServerId(): int { + return $this->serverId; + } + + /** + * @psalm-return int<0,4095> + */ + public function getSequenceId(): int { + return $this->sequenceId; + } + + public function isCli(): bool { + return $this->isCli; + } + + /** + * @psalm-return non-negative-int + */ + public function getSeconds(): int { + return $this->seconds; + } + + /** + * @psalm-return int<0,999> + */ + public function getMilliseconds(): int { + return $this->milliseconds; + } + + public function getCreatedAt(): \DateTimeImmutable { + return $this->createdAt; + } +} diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index 0bc64b3ee10ad..26187d82274a5 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -13,7 +13,7 @@ use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\Job; use OCP\Server; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; /** * Class DummyJobList @@ -46,7 +46,7 @@ public function add($job, $argument = null, ?int $firstCheck = null): void { $job = Server::get($job); } $job->setArgument($argument); - $job->setId(Server::get(IGenerator::class)->nextId()); + $job->setId(Server::get(ISnowflakeGenerator::class)->nextId()); if (!$this->has($job, null)) { $this->jobs[] = $job; } diff --git a/tests/lib/BackgroundJob/JobListTest.php b/tests/lib/BackgroundJob/JobListTest.php index ae3046f9b3c62..186de1ef29154 100644 --- a/tests/lib/BackgroundJob/JobListTest.php +++ b/tests/lib/BackgroundJob/JobListTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016-2025 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2016 ownCloud, Inc. * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -16,7 +16,7 @@ use OCP\IConfig; use OCP\IDBConnection; use OCP\Server; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; @@ -46,7 +46,7 @@ protected function setUp(): void { $this->config, $this->timeFactory, Server::get(LoggerInterface::class), - Server::get(IGenerator::class), + Server::get(ISnowflakeGenerator::class), ); } @@ -146,7 +146,7 @@ protected function createTempJob($class, if ($lastChecked === 0) { $lastChecked = time(); } - $id = Server::get(IGenerator::class)->nextId(); + $id = Server::get(ISnowflakeGenerator::class)->nextId(); $query = $this->connection->getQueryBuilder(); $query->insert('jobs') diff --git a/tests/lib/Preview/GeneratorTest.php b/tests/lib/Preview/GeneratorTest.php index 074a3272dbf32..ceaf483a6588c 100644 --- a/tests/lib/Preview/GeneratorTest.php +++ b/tests/lib/Preview/GeneratorTest.php @@ -22,7 +22,6 @@ 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; @@ -42,7 +41,6 @@ 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(); @@ -54,7 +52,6 @@ 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, @@ -64,7 +61,6 @@ 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 ee1bb0f9ab4af..b4b47c85983f8 100644 --- a/tests/lib/Preview/MovePreviewJobTest.php +++ b/tests/lib/Preview/MovePreviewJobTest.php @@ -24,7 +24,7 @@ use OCP\IConfig; use OCP\IDBConnection; use OCP\Server; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; @@ -124,7 +124,7 @@ public function testMigrationLegacyPath(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, - Server::get(IGenerator::class), + Server::get(ISnowflakeGenerator::class), Server::get(IAppDataFactory::class), ); $this->invokePrivate($job, 'run', [[]]); @@ -157,7 +157,7 @@ public function testMigrationPath(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, - Server::get(IGenerator::class), + Server::get(ISnowflakeGenerator::class), Server::get(IAppDataFactory::class) ); $this->invokePrivate($job, 'run', [[]]); @@ -198,7 +198,7 @@ public function testMigrationPathWithVersion(): void { $this->mimeTypeDetector, $this->mimeTypeLoader, $this->logger, - Server::get(IGenerator::class), + Server::get(ISnowflakeGenerator::class), Server::get(IAppDataFactory::class) ); $this->invokePrivate($job, 'run', [[]]); diff --git a/tests/lib/Preview/PreviewMapperTest.php b/tests/lib/Preview/PreviewMapperTest.php index 0a7fc6a5d78cc..9436c37982389 100644 --- a/tests/lib/Preview/PreviewMapperTest.php +++ b/tests/lib/Preview/PreviewMapperTest.php @@ -14,20 +14,20 @@ use OC\Preview\Db\PreviewMapper; use OCP\IDBConnection; use OCP\Server; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use Test\TestCase; #[\PHPUnit\Framework\Attributes\Group('DB')] class PreviewMapperTest extends TestCase { private PreviewMapper $previewMapper; private IDBConnection $connection; - private IGenerator $snowflake; + private ISnowflakeGenerator $snowflake; public function setUp(): void { parent::setUp(); $this->previewMapper = Server::get(PreviewMapper::class); $this->connection = Server::get(IDBConnection::class); - $this->snowflake = Server::get(IGenerator::class); + $this->snowflake = Server::get(ISnowflakeGenerator::class); $qb = $this->connection->getQueryBuilder(); $qb->delete('preview_locations')->executeStatement(); @@ -79,7 +79,7 @@ private function createPreviewForFileId(int $fileId, ?int $bucket = null): void $qb->executeStatement(); } $preview = new Preview(); - $preview->setId($this->snowflake->nextId()); + $preview->setId(); $preview->setFileId($fileId); $preview->setStorageId(1); $preview->setCropped(true); diff --git a/tests/lib/Preview/PreviewServiceTest.php b/tests/lib/Preview/PreviewServiceTest.php index 6106dbb7a043f..28e901f27c47f 100644 --- a/tests/lib/Preview/PreviewServiceTest.php +++ b/tests/lib/Preview/PreviewServiceTest.php @@ -14,7 +14,6 @@ use OC\Preview\Db\PreviewMapper; use OC\Preview\PreviewService; use OCP\Server; -use OCP\Snowflake\IGenerator; use PHPUnit\Framework\TestCase; #[CoversClass(PreviewService::class)] @@ -22,13 +21,11 @@ class PreviewServiceTest extends TestCase { private PreviewService $previewService; private PreviewMapper $previewMapper; - private IGenerator $snowflakeGenerator; protected function setUp(): void { parent::setUp(); $this->previewService = Server::get(PreviewService::class); $this->previewMapper = Server::get(PreviewMapper::class); - $this->snowflakeGenerator = Server::get(IGenerator::class); $this->previewService->deleteAll(); } @@ -40,7 +37,6 @@ 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/tests/lib/Snowflake/DecoderTest.php b/tests/lib/Snowflake/DecoderTest.php index 82035320c45a4..2c111a1b46f10 100644 --- a/tests/lib/Snowflake/DecoderTest.php +++ b/tests/lib/Snowflake/DecoderTest.php @@ -7,7 +7,7 @@ namespace Test\Snowflake; -use OC\Snowflake\Decoder; +use OC\Snowflake\SnowflakeDecoder; use PHPUnit\Framework\Attributes\DataProvider; use Test\TestCase; @@ -15,10 +15,10 @@ * @package Test */ class DecoderTest extends TestCase { - private Decoder $decoder; + private SnowflakeDecoder $decoder; public function setUp():void { - $this->decoder = new Decoder(); + $this->decoder = new SnowflakeDecoder(); } #[DataProvider('provideSnowflakeIds')] @@ -31,10 +31,10 @@ public function testDecode( ): void { $data = $this->decoder->decode($snowflakeId); - $this->assertEquals($timestamp, (float)$data['createdAt']->format('U.v')); - $this->assertEquals($serverId, $data['serverId']); - $this->assertEquals($sequenceId, $data['sequenceId']); - $this->assertEquals($isCli, $data['isCli']); + $this->assertEquals($timestamp, (float)$data->getCreatedAt()->format('U.v')); + $this->assertEquals($serverId, $data->getServerId()); + $this->assertEquals($sequenceId, $data->getSequenceId()); + $this->assertEquals($isCli, $data->isCli()); } public static function provideSnowflakeIds(): array { diff --git a/tests/lib/Snowflake/GeneratorTest.php b/tests/lib/Snowflake/GeneratorTest.php index ce1160b2867d4..6d073e31343db 100644 --- a/tests/lib/Snowflake/GeneratorTest.php +++ b/tests/lib/Snowflake/GeneratorTest.php @@ -10,12 +10,12 @@ namespace Test\Snowflake; use OC\AppFramework\Utility\TimeFactory; -use OC\Snowflake\Decoder; -use OC\Snowflake\Generator; use OC\Snowflake\ISequence; +use OC\Snowflake\SnowflakeDecoder; +use OC\Snowflake\SnowflakeGenerator; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; -use OCP\Snowflake\IGenerator; +use OCP\Snowflake\ISnowflakeGenerator; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -24,12 +24,12 @@ * @package Test */ class GeneratorTest extends TestCase { - private Decoder $decoder; + private SnowflakeDecoder $decoder; private IConfig&MockObject $config; private ISequence&MockObject $sequence; public function setUp():void { - $this->decoder = new Decoder(); + $this->decoder = new SnowflakeDecoder(); $this->config = $this->createMock(IConfig::class); $this->config->method('getSystemValueInt') @@ -43,27 +43,27 @@ public function setUp():void { } public function testGenerator(): void { - $generator = new Generator(new TimeFactory(), $this->config, $this->sequence); + $generator = new SnowflakeGenerator(new TimeFactory(), $this->config, $this->sequence); $snowflakeId = $generator->nextId(); $data = $this->decoder->decode($generator->nextId()); $this->assertIsString($snowflakeId); // Check timestamp - $this->assertGreaterThan(time() - 30, $data['createdAt']->format('U')); + $this->assertGreaterThan(time() - 30, $data->getCreatedAt()->format('U')); // Check serverId - $this->assertGreaterThanOrEqual(0, $data['serverId']); - $this->assertLessThanOrEqual(1023, $data['serverId']); + $this->assertGreaterThanOrEqual(0, $data->getServerId()); + $this->assertLessThanOrEqual(1023, $data->getServerId()); // Check sequenceId - $this->assertGreaterThanOrEqual(0, $data['sequenceId']); - $this->assertLessThanOrEqual(4095, $data['sequenceId']); + $this->assertGreaterThanOrEqual(0, $data->getSequenceId()); + $this->assertLessThanOrEqual(4095, $data->getSequenceId()); // Check CLI - $this->assertTrue($data['isCli']); + $this->assertTrue($data->isCli()); // Check serverId - $this->assertEquals(42, $data['serverId']); + $this->assertEquals(42, $data->getServerId()); } #[DataProvider('provideSnowflakeData')] @@ -72,12 +72,12 @@ public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, i $timeFactory = $this->createMock(ITimeFactory::class); $timeFactory->method('now')->willReturn($dt); - $generator = new Generator($timeFactory, $this->config, $this->sequence); + $generator = new SnowflakeGenerator($timeFactory, $this->config, $this->sequence); $data = $this->decoder->decode($generator->nextId()); - $this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET)); - $this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v')); - $this->assertEquals(42, $data['serverId']); + $this->assertEquals($expectedSeconds, ($data->getCreatedAt()->format('U') - ISnowflakeGenerator::TS_OFFSET)); + $this->assertEquals($expectedMilliseconds, (int)$data->getCreatedAt()->format('v')); + $this->assertEquals(42, $data->getServerId()); } public static function provideSnowflakeData(): array {