From 181cfd0023a156324ef00b2cbbe6a4d2a9160873 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 3 Dec 2025 06:24:07 +0530 Subject: [PATCH 1/4] Revert "Merge pull request #139 from utopia-php/fix-dir-exists-0.18.x" This reverts commit ba1ddb1cc03c40b5515541726e6a946d45201eed, reversing changes made to 3d8ce53ae042173bf230445e996056c5f65ded22. --- tests/Storage/S3Base.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/Storage/S3Base.php b/tests/Storage/S3Base.php index 2218ce0c..314cfe02 100644 --- a/tests/Storage/S3Base.php +++ b/tests/Storage/S3Base.php @@ -20,11 +20,6 @@ abstract protected function getAdapterName(): string; */ abstract protected function getAdapterDescription(): string; - /** - * @return string - */ - abstract protected function getAdapterType(): string; - /** * @var S3 */ From e956e77ecbcc933de1e063ff48e3a320ab6b1b4c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 16 Dec 2025 22:37:03 +0530 Subject: [PATCH 2/4] chore: throw custom notfound exception --- src/Storage/Device/Local.php | 3 +- src/Storage/Device/S3.php | 34 +++++++++++++++++++-- src/Storage/Exception/NotFoundException.php | 9 ++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/Storage/Exception/NotFoundException.php diff --git a/src/Storage/Device/Local.php b/src/Storage/Device/Local.php index 84afb7b4..ef92d815 100644 --- a/src/Storage/Device/Local.php +++ b/src/Storage/Device/Local.php @@ -4,6 +4,7 @@ use Exception; use Utopia\Storage\Device; +use Utopia\Storage\Exception\NotFoundException; use Utopia\Storage\Storage; class Local extends Device @@ -272,7 +273,7 @@ public function abort(string $path, string $extra = ''): bool public function read(string $path, int $offset = 0, int $length = null): string { if (! $this->exists($path)) { - throw new Exception('File Not Found'); + throw new NotFoundException('File not found'); } return \file_get_contents($path, use_include_path: false, context: null, offset: $offset, length: $length); diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index f24e6cf6..8e303344 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -4,6 +4,7 @@ use Exception; use Utopia\Storage\Device; +use Utopia\Storage\Exception\NotFoundException; use Utopia\Storage\Storage; class S3 extends Device @@ -284,7 +285,7 @@ public function transfer(string $path, string $destination, Device $device): boo try { $response = $this->getInfo($path); } catch (\Throwable $e) { - throw new Exception('File not found'); + throw new NotFoundException('File not found'); } $size = (int) ($response['content-length'] ?? 0); $contentType = $response['content-type'] ?? ''; @@ -903,7 +904,7 @@ protected function call(string $operation, string $method, string $uri, string $ } if ($response->code >= 400) { - throw new Exception($response->body, $response->code); + $this->parseAndThrowS3Error($response->body, $response->code); } // Parse body into XML @@ -927,6 +928,35 @@ protected function call(string $operation, string $method, string $uri, string $ } } + /** + * Parse S3 XML error response and throw appropriate exception + * + * @param string $errorBody The error response body + * @param int $statusCode The HTTP status code + * @throws NotFoundException When the error is NoSuchKey + * @throws Exception For other S3 errors + */ + private function parseAndThrowS3Error(string $errorBody, int $statusCode): void + { + if (str_starts_with($errorBody, 'Code ?? ''); + $errorMessage = (string) ($xml->Message ?? ''); + + if ($errorCode === 'NoSuchKey') { + throw new NotFoundException($errorMessage ?: 'File not found', $statusCode); + } + } catch (NotFoundException $e) { + throw $e; + } catch (\Throwable $e) { + // If XML parsing fails, fall through to original error + } + } + + throw new Exception($errorBody, $statusCode); + } + /** * Sort compare for meta headers * diff --git a/src/Storage/Exception/NotFoundException.php b/src/Storage/Exception/NotFoundException.php new file mode 100644 index 00000000..d09ca44a --- /dev/null +++ b/src/Storage/Exception/NotFoundException.php @@ -0,0 +1,9 @@ + Date: Tue, 16 Dec 2025 22:40:24 +0530 Subject: [PATCH 3/4] lint --- src/Storage/Device/S3.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index 8e303344..c6425ae5 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -931,8 +931,9 @@ protected function call(string $operation, string $method, string $uri, string $ /** * Parse S3 XML error response and throw appropriate exception * - * @param string $errorBody The error response body - * @param int $statusCode The HTTP status code + * @param string $errorBody The error response body + * @param int $statusCode The HTTP status code + * * @throws NotFoundException When the error is NoSuchKey * @throws Exception For other S3 errors */ From f8afc6d27692fc1e0b2cfb260669c2fa3cba99b6 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 16 Dec 2025 22:52:42 +0530 Subject: [PATCH 4/4] fix errors --- tests/Storage/Device/LocalTest.php | 7 +++++++ tests/Storage/S3Base.php | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/tests/Storage/Device/LocalTest.php b/tests/Storage/Device/LocalTest.php index 30a721fc..b69104eb 100644 --- a/tests/Storage/Device/LocalTest.php +++ b/tests/Storage/Device/LocalTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Utopia\Storage\Device\AWS; use Utopia\Storage\Device\Local; +use Utopia\Storage\Exception\NotFoundException; class LocalTest extends TestCase { @@ -75,6 +76,12 @@ public function testRead() $this->object->delete($this->object->getPath('text-for-read.txt')); } + public function testReadNonExistentFile() + { + $this->expectException(NotFoundException::class); + $this->object->read($this->object->getPath('non-existent-file.txt')); + } + public function testFileExists() { $this->assertEquals($this->object->write($this->object->getPath('text-for-test-exists.txt'), 'Hello World'), true); diff --git a/tests/Storage/S3Base.php b/tests/Storage/S3Base.php index 314cfe02..f7535a53 100644 --- a/tests/Storage/S3Base.php +++ b/tests/Storage/S3Base.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Utopia\Storage\Device\Local; use Utopia\Storage\Device\S3; +use Utopia\Storage\Exception\NotFoundException; abstract class S3Base extends TestCase { @@ -133,6 +134,12 @@ public function testRead() $this->object->delete($this->object->getPath('text-for-read.txt')); } + public function testReadNonExistentFile() + { + $this->expectException(NotFoundException::class); + $this->object->read($this->object->getPath('non-existent-file.txt')); + } + public function testFileExists() { $this->assertEquals(true, $this->object->exists($this->object->getPath('testing/kitten-1.jpg'))); @@ -410,4 +417,15 @@ public function testTransferSmall() $this->object->delete($path); $device->delete($destination); } + + public function testTransferNonExistentFile() + { + $device = new Local(__DIR__.'/../resources/disk-a'); + + $path = $this->object->getPath('non-existent-file.txt'); + $destination = $device->getPath('hello.txt'); + + $this->expectException(NotFoundException::class); + $this->object->transfer($path, $destination, $device); + } }