Skip to content

Commit

Permalink
Its now possible complete multipart upload
Browse files Browse the repository at this point in the history
  • Loading branch information
mreduar committed Jul 21, 2024
1 parent 7e49bd5 commit d862f04
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 4 deletions.
2 changes: 2 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
Route::get('s3m/create-multipart-upload', [S3MultipartController::class, 'createMultipartUpload'])->name('s3m.create-multipart');

Route::get('s3m/create-sign-part', [S3MultipartController::class, 'signPartUpload'])->name('s3m.create-sign-part');

Route::post('s3m/complete-multipart-upload', [S3MultipartController::class, 'completeMultipartUpload'])->name('s3m.complete-multipart');
30 changes: 28 additions & 2 deletions src/Http/Controllers/S3MultipartController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Support\Str;
use InvalidArgumentException;
use MrEduar\LaravelS3Multipart\Contracts\StorageMultipartUploadControllerContract;
use MrEduar\LaravelS3Multipart\Http\Requests\CompleteMultipartUploadRequest;
use MrEduar\LaravelS3Multipart\Http\Requests\CreateMultipartUploadRequest;
use MrEduar\LaravelS3Multipart\Http\Requests\SignPartRequest;

Expand Down Expand Up @@ -89,9 +90,34 @@ public function signPartUpload(SignPartRequest $request): JsonResponse
/**
* Complete a multipart upload.
*/
public function completeMultipartUpload(Request $request): JsonResponse
public function completeMultipartUpload(CompleteMultipartUploadRequest $request): JsonResponse
{
return new JsonResponse([]);
$this->ensureEnvironmentVariablesAreAvailable($request);

$bucket = $request->input('bucket') ?: $_ENV['AWS_BUCKET'];

$client = $this->storageClient();

try {
$completeUpload = $client->completeMultipartUpload([
'Bucket' => $bucket,
'Key' => $request->input('key'),
'UploadId' => $request->input('upload_id'),
'MultipartUpload' => [
'Parts' => $request->input('parts'),
],
]);

return response()->json([
'url' => $completeUpload['Location'],
'bucket' => $bucket,
'key' => $request->input('key'),
]);
} catch (Exception $e) {
return response()->json([
'error' => $e->getMessage(),
], 500);
}
}

/**
Expand Down
10 changes: 8 additions & 2 deletions src/Http/Requests/CompleteMultipartUploadRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace MrEduar\LaravelS3Multipart\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;

class CompleteMultipartUploadRequest extends FormRequest
{
Expand All @@ -13,7 +14,7 @@ class CompleteMultipartUploadRequest extends FormRequest
*/
public function authorize()
{
return true;
return Gate::allows('uploadFiles', [$this->user(), $this->input('bucket')]);
}

/**
Expand All @@ -24,7 +25,12 @@ public function authorize()
public function rules()
{
return [
//
'bucket' => ['nullable', 'string'],
'key' => ['required', 'string'],
'upload_id' => ['required', 'string'],
'parts' => ['required', 'array'],
'parts.*.PartNumber' => ['required', 'integer'],
'parts.*.ETag' => ['required', 'string'],
];
}
}
56 changes: 56 additions & 0 deletions tests/Isolated/S3MultipartControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Illuminate\Testing\Fluent\AssertableJson;

use function Pest\Laravel\getJson;
use function Pest\Laravel\postJson;
use function Pest\Laravel\withoutExceptionHandling;

beforeEach(function () {
Config::set([
Expand Down Expand Up @@ -149,3 +151,57 @@
'error' => 'Upload not found',
]);
});

it('can complete multipart upload', function () {
$mock = Mockery::mock('overload:'.Aws\S3\S3Client::class);

$mock->shouldReceive('completeMultipartUpload')->once()->andReturn([
'Location' => 'https://example.com',
]);

$this->app->instance(Aws\S3\S3Client::class, $mock);

postJson(route('s3m.complete-multipart'), [
'key' => $key = Str::uuid()->toString(),
'upload_id' => Str::random(),
'parts' => [
['ETag' => Str::random(), 'PartNumber' => 1],
['ETag' => Str::random(), 'PartNumber' => 2],
],
])->assertOk()
->assertJson(fn (AssertableJson $json) => $json
->has('bucket')
->where('key', $key)
->where('url', 'https://example.com')
);
});

it('complete multipart catched exceptions', function () {
$mock = Mockery::mock('overload:'.Aws\S3\S3Client::class);
$mock->shouldReceive('completeMultipartUpload')->once()->andThrow(new Exception('Upload not found'));

$this->app->instance(Aws\S3\S3Client::class, $mock);

postJson(route('s3m.complete-multipart'), [
'key' => Str::uuid()->toString(),
'upload_id' => Str::random(),
'parts' => [
['ETag' => Str::random(), 'PartNumber' => 1],
['ETag' => Str::random(), 'PartNumber' => 2],
],
])
->assertJson([
'error' => 'Upload not found',
]);
});

it('throw an exception when none of the required env variables are set', function () {
unset($_ENV['AWS_BUCKET']);
unset($_ENV['AWS_DEFAULT_REGION']);
unset($_ENV['AWS_ACCESS_KEY_ID']);
unset($_ENV['AWS_SECRET_ACCESS_KEY']);

withoutExceptionHandling();

getJson(route('storage.create.multipart'));
})->throws(InvalidArgumentException::class);

0 comments on commit d862f04

Please sign in to comment.