From 7781e7562754c88efd59b32ad177edf4c8486d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 31 Oct 2024 18:57:08 +0100 Subject: [PATCH 1/2] PHPLIB-1568 Add `GridFS\Bucket::deleteByName(filename)` and `renameByName(filename, newFilename)` (#1504) --- psalm-baseline.xml | 11 +++++++ src/GridFS/Bucket.php | 35 ++++++++++++++++++++ src/GridFS/CollectionWrapper.php | 7 ++-- tests/GridFS/BucketFunctionalTest.php | 46 +++++++++++++++++++++++++++ tests/UnifiedSpecTests/Operation.php | 14 ++++++++ tests/UnifiedSpecTests/Util.php | 2 ++ 6 files changed, 110 insertions(+), 5 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 039b43007..ba4f3cb4f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -284,9 +284,20 @@ + + + + + + + filesCollection->updateMany( + ['filename' => $filename], + ['$set' => ['filename' => $newFilename]], + )->getMatchedCount()]]> + diff --git a/src/GridFS/Bucket.php b/src/GridFS/Bucket.php index 56a4e26e2..85046906c 100644 --- a/src/GridFS/Bucket.php +++ b/src/GridFS/Bucket.php @@ -242,6 +242,23 @@ public function delete(mixed $id) } } + /** + * Delete all the revisions of a file name from the GridFS bucket. + * + * @param string $filename Filename + * + * @throws FileNotFoundException if no file could be selected + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function deleteByName(string $filename): void + { + $count = $this->collectionWrapper->deleteFileAndChunksByFilename($filename); + + if ($count === 0) { + throw FileNotFoundException::byFilename($filename); + } + } + /** * Writes the contents of a GridFS file to a writable stream. * @@ -648,6 +665,24 @@ public function rename(mixed $id, string $newFilename) } } + /** + * Renames all the revisions of a file name in the GridFS bucket. + * + * @param string $filename Filename + * @param string $newFilename New filename + * + * @throws FileNotFoundException if no file could be selected + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function renameByName(string $filename, string $newFilename): void + { + $count = $this->collectionWrapper->updateFilenameForFilename($filename, $newFilename); + + if ($count === 0) { + throw FileNotFoundException::byFilename($filename); + } + } + /** * Writes the contents of a readable stream to a GridFS file. * diff --git a/src/GridFS/CollectionWrapper.php b/src/GridFS/CollectionWrapper.php index 0c6b9b96c..09b06ba23 100644 --- a/src/GridFS/CollectionWrapper.php +++ b/src/GridFS/CollectionWrapper.php @@ -74,7 +74,7 @@ public function deleteChunksByFilesId(mixed $id): void /** * Delete all GridFS files and chunks for a given filename. */ - public function deleteFileAndChunksByFilename(string $filename): ?int + public function deleteFileAndChunksByFilename(string $filename): int { /** @var iterable $files */ $files = $this->findFiles(['filename' => $filename], [ @@ -150,9 +150,6 @@ public function findChunksByFileId(mixed $id, int $fromChunk = 0) */ public function findFileByFilenameAndRevision(string $filename, int $revision): ?object { - $filename = $filename; - $revision = $revision; - if ($revision < 0) { $skip = abs($revision) - 1; $sortOrder = -1; @@ -266,7 +263,7 @@ public function insertFile(array|object $file): void /** * Updates the filename field in the file document for all the files with a given filename. */ - public function updateFilenameForFilename(string $filename, string $newFilename): ?int + public function updateFilenameForFilename(string $filename, string $newFilename): int { return $this->filesCollection->updateMany( ['filename' => $filename], diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php index 0b85b8d9a..8faef8e38 100644 --- a/tests/GridFS/BucketFunctionalTest.php +++ b/tests/GridFS/BucketFunctionalTest.php @@ -160,6 +160,34 @@ public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expected $this->assertCollectionCount($this->chunksCollection, 0); } + public function testDeleteByName(): void + { + $this->bucket->uploadFromStream('filename', self::createStream('foobar1')); + $this->bucket->uploadFromStream('filename', self::createStream('foobar2')); + $this->bucket->uploadFromStream('filename', self::createStream('foobar3')); + + $this->bucket->uploadFromStream('other', self::createStream('foobar')); + + $this->assertCollectionCount($this->filesCollection, 4); + $this->assertCollectionCount($this->chunksCollection, 4); + + $this->bucket->deleteByName('filename'); + + $this->assertCollectionCount($this->filesCollection, 1); + $this->assertCollectionCount($this->chunksCollection, 1); + + $this->bucket->deleteByName('other'); + + $this->assertCollectionCount($this->filesCollection, 0); + $this->assertCollectionCount($this->chunksCollection, 0); + } + + public function testDeleteByNameShouldRequireFileToExist(): void + { + $this->expectException(FileNotFoundException::class); + $this->bucket->deleteByName('nonexistent-name'); + } + public function testDownloadingFileWithMissingChunk(): void { $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar')); @@ -723,6 +751,24 @@ public function testRenameShouldRequireFileToExist(): void $this->bucket->rename('nonexistent-id', 'b'); } + public function testRenameByName(): void + { + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + + $this->bucket->renameByName('filename', 'newname'); + + $this->assertNull($this->bucket->findOne(['filename' => 'filename']), 'No file has the old name'); + $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('newname')); + } + + public function testRenameByNameShouldRequireFileToExist(): void + { + $this->expectException(FileNotFoundException::class); + $this->bucket->renameByName('nonexistent-name', 'b'); + } + public function testUploadFromStream(): void { $options = [ diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php index 390e278be..d2550f498 100644 --- a/tests/UnifiedSpecTests/Operation.php +++ b/tests/UnifiedSpecTests/Operation.php @@ -789,6 +789,12 @@ private function executeForBucket(Bucket $bucket) return $bucket->delete($args['id']); + case 'deleteByName': + assertArrayHasKey('filename', $args); + assertIsString($args['filename']); + + return $bucket->deleteByName($args['filename']); + case 'downloadByName': assertArrayHasKey('filename', $args); assertIsString($args['filename']); @@ -812,6 +818,14 @@ private function executeForBucket(Bucket $bucket) return null; + case 'renameByName': + assertArrayHasKey('filename', $args); + assertArrayHasKey('newFilename', $args); + assertIsString($args['filename']); + assertIsString($args['newFilename']); + + return $bucket->renameByName($args['filename'], $args['newFilename']); + case 'uploadWithId': assertArrayHasKey('id', $args); $args['_id'] = $args['id']; diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php index e09e3f942..b0d575d39 100644 --- a/tests/UnifiedSpecTests/Util.php +++ b/tests/UnifiedSpecTests/Util.php @@ -132,9 +132,11 @@ final class Util ], Bucket::class => [ 'delete' => ['id'], + 'deleteByName' => ['filename'], 'downloadByName' => ['filename', 'revision'], 'download' => ['id'], 'rename' => ['id', 'newFilename'], + 'renameByName' => ['filename', 'newFilename'], 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], 'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], ], From 8a687f249d5dfff878bed4e6aeb28937a591eb48 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 4 Nov 2024 10:01:57 +0100 Subject: [PATCH 2/2] Remove obsolete baseline entries --- psalm-baseline.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ec26f6503..6521b8544 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -275,20 +275,9 @@ - - - - - - - filesCollection->updateMany( - ['filename' => $filename], - ['$set' => ['filename' => $newFilename]], - )->getMatchedCount()]]> -