Skip to content

Commit

Permalink
AsyncS3: throw FilesystemOperationFailed exceptions in case upload or…
Browse files Browse the repository at this point in the history
… listContent fails
  • Loading branch information
glaubinix committed Sep 27, 2024
1 parent 22af5a3 commit 98ec43f
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 33 deletions.
88 changes: 55 additions & 33 deletions src/AsyncAwsS3/AsyncAwsS3Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@
use League\Flysystem\UnableToCheckDirectoryExistence;
use League\Flysystem\UnableToCheckFileExistence;
use League\Flysystem\UnableToCopyFile;
use League\Flysystem\UnableToCreateDirectory;
use League\Flysystem\UnableToDeleteDirectory;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToGeneratePublicUrl;
use League\Flysystem\UnableToGenerateTemporaryUrl;
use League\Flysystem\UnableToListContents;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToProvideChecksum;
use League\Flysystem\UnableToReadFile;
use League\Flysystem\UnableToRetrieveMetadata;
use League\Flysystem\UnableToSetVisibility;
use League\Flysystem\UnableToWriteFile;
use League\Flysystem\UrlGeneration\PublicUrlGenerator;
use League\Flysystem\UrlGeneration\TemporaryUrlGenerator;
use League\Flysystem\Visibility;
Expand Down Expand Up @@ -177,32 +181,42 @@ public function deleteDirectory(string $path): void

$objects = [];
$params = ['Bucket' => $this->bucket, 'Prefix' => $prefix];
$result = $this->client->listObjectsV2($params);
/** @var AwsObject $item */
foreach ($result->getContents() as $item) {
$key = $item->getKey();
if (null !== $key) {
$objects[] = new ObjectIdentifier(['Key' => $key]);

try {
$result = $this->client->listObjectsV2($params);
/** @var AwsObject $item */
foreach ($result->getContents() as $item) {
$key = $item->getKey();
if (null !== $key) {
$objects[] = new ObjectIdentifier(['Key' => $key]);
}
}
}

if (empty($objects)) {
return;
}
if (empty($objects)) {
return;
}

foreach (array_chunk($objects, 1000) as $chunk) {
$this->client->deleteObjects([
'Bucket' => $this->bucket,
'Delete' => ['Objects' => $chunk],
]);
foreach (array_chunk($objects, 1000) as $chunk) {
$this->client->deleteObjects([
'Bucket' => $this->bucket,
'Delete' => ['Objects' => $chunk],
]);
}
} catch (\Throwable $e) {
throw UnableToDeleteDirectory::atLocation($path, $e->getMessage(), $e);
}
}

public function createDirectory(string $path, Config $config): void
{
$defaultVisibility = $config->get(Config::OPTION_DIRECTORY_VISIBILITY, $this->visibility->defaultForDirectories());
$config = $config->withDefaults([Config::OPTION_VISIBILITY => $defaultVisibility]);
$this->upload(rtrim($path, '/') . '/', '', $config);

try {
$this->upload(rtrim($path, '/') . '/', '', $config);
} catch (Throwable $e) {
throw UnableToCreateDirectory::dueToFailure($path, $e);
}
}

public function setVisibility(string $path, string $visibility): void
Expand Down Expand Up @@ -292,16 +306,20 @@ public function listContents(string $path, bool $deep): iterable
$options['Delimiter'] = '/';
}

$listing = $this->retrievePaginatedListing($options);
try {
$listing = $this->retrievePaginatedListing($options);

foreach ($listing as $item) {
$item = $this->mapS3ObjectMetadata($item);
foreach ($listing as $item) {
$item = $this->mapS3ObjectMetadata($item);

if ($item->path() === $path) {
continue;
}
if ($item->path() === $path) {
continue;
}

yield $item;
yield $item;
}
} catch (\Throwable $e) {
throw UnableToListContents::atLocation($path, $deep, $e);
}
}

Expand Down Expand Up @@ -363,16 +381,20 @@ private function upload(string $path, $body, Config $config): void
$options['ContentType'] = $mimeType;
}

if ($this->client instanceof SimpleS3Client) {
// Supports upload of files larger than 5GB
$this->client->upload($this->bucket, $key, $body, array_merge($options, ['ACL' => $acl]));
} else {
$this->client->putObject(array_merge($options, [
'Bucket' => $this->bucket,
'Key' => $key,
'Body' => $body,
'ACL' => $acl,
]));
try {
if ($this->client instanceof SimpleS3Client) {
// Supports upload of files larger than 5GB
$this->client->upload($this->bucket, $key, $body, array_merge($options, ['ACL' => $acl]));
} else {
$this->client->putObject(array_merge($options, [
'Bucket' => $this->bucket,
'Key' => $key,
'Body' => $body,
'ACL' => $acl,
]));
}
} catch (Throwable $exception) {
throw UnableToWriteFile::atLocation($path, $exception->getMessage(), $exception);
}
}

Expand Down
39 changes: 39 additions & 0 deletions src/AsyncAwsS3/AsyncAwsS3AdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
use League\Flysystem\StorageAttributes;
use League\Flysystem\UnableToCheckFileExistence;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToListContents;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToRetrieveMetadata;
use League\Flysystem\UnableToWriteFile;
use League\Flysystem\Visibility;
use function getenv;
use function iterator_to_array;
Expand Down Expand Up @@ -324,6 +326,30 @@ public function write_with_simple_s3_client(): void
$filesystem->write($file, $contents, new Config());
}

/**
* @test
*/
public function failing_to_write_a_file(): void
{
$file = 'foo/bar.txt';
$prefix = 'all-files';
$bucket = 'foobar';
$contents = 'contents';

$s3Client = $this->getMockBuilder(S3Client::class)
->disableOriginalConstructor()
->onlyMethods(['putObject'])
->getMock();
$s3Client->expects(self::once())
->method('putObject')
->willThrowException(new \RuntimeException());

$this->expectException(UnableToWriteFile::class);

$filesystem = new AsyncAwsS3Adapter($s3Client, $bucket, $prefix);
$filesystem->write($file, $contents, new Config());
}

/**
* @test
*/
Expand Down Expand Up @@ -392,6 +418,19 @@ public function top_level_directory_excluded_from_listing(): void
});
}

/**
* @test
*/
public function failing_to_list_contents(): void
{
$adapter = $this->adapter();
static::$stubS3Client->throwExceptionWhenExecutingCommand('ListObjectsV2');

$this->expectException(UnableToListContents::class);

iterator_to_array($adapter->listContents('/path', false));
}

protected static function createFilesystemAdapter(): FilesystemAdapter
{
static::$stubS3Client = new S3ClientStub(static::s3Client(), self::awsConfig());
Expand Down

0 comments on commit 98ec43f

Please sign in to comment.