Media Endpoint improvements

For the code, media related stuff is now in its own controller

Added support for querying the most recent file uploaded
This commit is contained in:
Jonny Barnes 2020-06-27 14:13:33 +01:00
parent 2193e96274
commit 4f2d3b7c2b
6 changed files with 496 additions and 338 deletions

View file

@ -5,20 +5,14 @@ declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Exceptions\InvalidTokenException; use App\Exceptions\InvalidTokenException;
use App\Jobs\ProcessMedia; use App\Http\Responses\MicropubResponses;
use App\Models\{Media, Place}; use App\Models\Place;
use App\Services\Micropub\{HCardService, HEntryService, UpdateService}; use App\Services\Micropub\{HCardService, HEntryService, UpdateService};
use App\Services\TokenService; use App\Services\TokenService;
use Exception; use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\{File, JsonResponse, Response, UploadedFile};
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Exception\NotReadableException;
use Intervention\Image\ImageManager;
use Monolog\Handler\StreamHandler; use Monolog\Handler\StreamHandler;
use Monolog\Logger; use Monolog\Logger;
use MStaack\LaravelPostgis\Geometries\Point; use MStaack\LaravelPostgis\Geometries\Point;
use Ramsey\Uuid\Uuid;
class MicropubController extends Controller class MicropubController extends Controller
{ {
@ -44,24 +38,31 @@ class MicropubController extends Controller
* then passes over the info to the relevant Service class. * then passes over the info to the relevant Service class.
* *
* @return JsonResponse * @return JsonResponse
* @throws InvalidTokenException
*/ */
public function post(): JsonResponse public function post(): JsonResponse
{ {
try { try {
$tokenData = $this->tokenService->validateToken(request()->input('access_token')); $tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) { } catch (InvalidTokenException $e) {
return $this->invalidTokenResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->invalidTokenResponse();
} }
if ($tokenData->hasClaim('scope') === false) { if ($tokenData->hasClaim('scope') === false) {
return $this->tokenHasNoScopeResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->tokenHasNoScopeResponse();
} }
$this->logMicropubRequest(request()->all()); $this->logMicropubRequest(request()->all());
if ((request()->input('h') == 'entry') || (request()->input('type.0') == 'h-entry')) { if ((request()->input('h') == 'entry') || (request()->input('type.0') == 'h-entry')) {
if (stristr($tokenData->getClaim('scope'), 'create') === false) { if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->insufficientScopeResponse();
} }
$location = $this->hentryService->process(request()->all(), $this->getCLientId()); $location = $this->hentryService->process(request()->all(), $this->getCLientId());
@ -73,7 +74,9 @@ class MicropubController extends Controller
if (request()->input('h') == 'card' || request()->input('type.0') == 'h-card') { if (request()->input('h') == 'card' || request()->input('type.0') == 'h-card') {
if (stristr($tokenData->getClaim('scope'), 'create') === false) { if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->insufficientScopeResponse();
} }
$location = $this->hcardService->process(request()->all()); $location = $this->hcardService->process(request()->all());
@ -85,7 +88,9 @@ class MicropubController extends Controller
if (request()->input('action') == 'update') { if (request()->input('action') == 'update') {
if (stristr($tokenData->getClaim('scope'), 'update') === false) { if (stristr($tokenData->getClaim('scope'), 'update') === false) {
return $this->insufficientScopeResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->insufficientScopeResponse();
} }
return $this->updateService->process(request()->all()); return $this->updateService->process(request()->all());
@ -112,7 +117,9 @@ class MicropubController extends Controller
try { try {
$tokenData = $this->tokenService->validateToken(request()->input('access_token')); $tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) { } catch (InvalidTokenException $e) {
return $this->invalidTokenResponse(); $micropubResponses = new MicropubResponses();
return $micropubResponses->invalidTokenResponse();
} }
if (request()->input('q') === 'syndicate-to') { if (request()->input('q') === 'syndicate-to') {
@ -154,131 +161,11 @@ class MicropubController extends Controller
]); ]);
} }
/**
* Process a media item posted to the media endpoint.
*
* @return JsonResponse
* @throws BindingResolutionException
* @throws Exception
*/
public function media(): JsonResponse
{
try {
$tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) {
return $this->invalidTokenResponse();
}
if ($tokenData->hasClaim('scope') === false) {
return $this->tokenHasNoScopeResponse();
}
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->insufficientScopeResponse();
}
if ((request()->hasFile('file') && request()->file('file')->isValid()) === false) {
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The uploaded file failed validation',
], 400);
}
$this->logMicropubRequest(request()->all());
$filename = $this->saveFile(request()->file('file'));
$manager = resolve(ImageManager::class);
try {
$image = $manager->make(request()->file('file'));
$width = $image->width();
} catch (NotReadableException $exception) {
// not an image
$width = null;
}
$media = Media::create([
'token' => request()->bearerToken(),
'path' => 'media/' . $filename,
'type' => $this->getFileTypeFromMimeType(request()->file('file')->getMimeType()),
'image_widths' => $width,
]);
// put the file on S3 initially, the ProcessMedia job may edit this
Storage::disk('s3')->putFileAs(
'media',
new File(storage_path('app') . '/' . $filename),
$filename
);
ProcessMedia::dispatch($filename);
return response()->json([
'response' => 'created',
'location' => $media->url,
], 201)->header('Location', $media->url);
}
/**
* Return the relevant CORS headers to a pre-flight OPTIONS request.
*
* @return Response
*/
public function mediaOptionsResponse(): Response
{
return response('OK', 200);
}
/**
* Get the file type from the mime-type of the uploaded file.
*
* @param string $mimeType
* @return string
*/
private function getFileTypeFromMimeType(string $mimeType): string
{
//try known images
$imageMimeTypes = [
'image/gif',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/tiff',
'image/webp',
];
if (in_array($mimeType, $imageMimeTypes)) {
return 'image';
}
//try known video
$videoMimeTypes = [
'video/mp4',
'video/mpeg',
'video/ogg',
'video/quicktime',
'video/webm',
];
if (in_array($mimeType, $videoMimeTypes)) {
return 'video';
}
//try known audio types
$audioMimeTypes = [
'audio/midi',
'audio/mpeg',
'audio/ogg',
'audio/x-m4a',
];
if (in_array($mimeType, $audioMimeTypes)) {
return 'audio';
}
return 'download';
}
/** /**
* Determine the client id from the access token sent with the request. * Determine the client id from the access token sent with the request.
* *
* @return string * @return string
* @throws InvalidTokenException
*/ */
private function getClientId(): string private function getClientId(): string
{ {
@ -295,64 +182,7 @@ class MicropubController extends Controller
private function logMicropubRequest(array $request) private function logMicropubRequest(array $request)
{ {
$logger = new Logger('micropub'); $logger = new Logger('micropub');
$logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')), Logger::DEBUG); $logger->pushHandler(new StreamHandler(storage_path('logs/micropub.log')));
$logger->debug('MicropubLog', $request); $logger->debug('MicropubLog', $request);
} }
/**
* Save an uploaded file to the local disk.
*
* @param UploadedFile $file
* @return string
* @throws Exception
*/
private function saveFile(UploadedFile $file): string
{
$filename = Uuid::uuid4()->toString() . '.' . $file->extension();
Storage::disk('local')->putFileAs('', $file, $filename);
return $filename;
}
/**
* Generate a response to be returned when the token has insufficient scope.
*
* @return JsonResponse
*/
private function insufficientScopeResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'insufficient_scope',
'error_description' => 'The tokens scope does not have the necessary requirements.',
], 401);
}
/**
* Generate a response to be returned when the token is invalid.
*
* @return JsonResponse
*/
private function invalidTokenResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
/**
* Generate a response to be returned when the token has no scope.
*
* @return JsonResponse
*/
private function tokenHasNoScopeResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The provided token has no scopes',
], 400);
}
} }

View file

@ -0,0 +1,209 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Exceptions\InvalidTokenException;
use App\Http\Responses\MicropubResponses;
use App\Jobs\ProcessMedia;
use App\Models\Media;
use App\Services\TokenService;
use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\File;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Exception\NotReadableException;
use Intervention\Image\ImageManager;
use Ramsey\Uuid\Uuid;
class MicropubMediaController extends Controller
{
protected TokenService $tokenService;
public function __construct(TokenService $tokenService)
{
$this->tokenService = $tokenService;
}
public function getHandler(): JsonResponse
{
try {
$tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->invalidTokenResponse();
}
if ($tokenData->hasClaim('scope') === false) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->tokenHasNoScopeResponse();
}
if (Str::contains($tokenData->getClaim('scope'), 'create') === false) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->insufficientScopeResponse();
}
if (request()->input('q') === 'last') {
$media = Media::latest()->firstOrFail();
return response()->json(['url' => $media->url]);
}
}
/**
* Process a media item posted to the media endpoint.
*
* @return JsonResponse
* @throws BindingResolutionException
* @throws Exception
*/
public function media(): JsonResponse
{
try {
$tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->invalidTokenResponse();
}
if ($tokenData->hasClaim('scope') === false) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->tokenHasNoScopeResponse();
}
if (Str::contains($tokenData->getClaim('scope'), 'create') === false) {
$micropubResponses = new MicropubResponses();
return $micropubResponses->insufficientScopeResponse();
}
if (request()->hasFile('file') === false) {
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'No file was sent with the request',
], 400);
}
if (request()->file('file')->isValid() === false) {
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The uploaded file failed validation',
], 400);
}
$filename = $this->saveFile(request()->file('file'));
$manager = resolve(ImageManager::class);
try {
$image = $manager->make(request()->file('file'));
$width = $image->width();
} catch (NotReadableException $exception) {
// not an image
$width = null;
}
$media = Media::create([
'token' => request()->bearerToken(),
'path' => 'media/' . $filename,
'type' => $this->getFileTypeFromMimeType(request()->file('file')->getMimeType()),
'image_widths' => $width,
]);
// put the file on S3 initially, the ProcessMedia job may edit this
Storage::disk('s3')->putFileAs(
'media',
new File(storage_path('app') . '/' . $filename),
$filename
);
ProcessMedia::dispatch($filename);
return response()->json([
'response' => 'created',
'location' => $media->url,
], 201)->header('Location', $media->url);
}
/**
* Return the relevant CORS headers to a pre-flight OPTIONS request.
*
* @return Response
*/
public function mediaOptionsResponse(): Response
{
return response('OK', 200);
}
/**
* Get the file type from the mime-type of the uploaded file.
*
* @param string $mimeType
* @return string
*/
private function getFileTypeFromMimeType(string $mimeType): string
{
//try known images
$imageMimeTypes = [
'image/gif',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/tiff',
'image/webp',
];
if (in_array($mimeType, $imageMimeTypes)) {
return 'image';
}
//try known video
$videoMimeTypes = [
'video/mp4',
'video/mpeg',
'video/ogg',
'video/quicktime',
'video/webm',
];
if (in_array($mimeType, $videoMimeTypes)) {
return 'video';
}
//try known audio types
$audioMimeTypes = [
'audio/midi',
'audio/mpeg',
'audio/ogg',
'audio/x-m4a',
];
if (in_array($mimeType, $audioMimeTypes)) {
return 'audio';
}
return 'download';
}
/**
* Save an uploaded file to the local disk.
*
* @param UploadedFile $file
* @return string
* @throws Exception
*/
private function saveFile(UploadedFile $file): string
{
$filename = Uuid::uuid4()->toString() . '.' . $file->extension();
Storage::disk('local')->putFileAs('', $file, $filename);
return $filename;
}
}

View file

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Http\Responses;
use Illuminate\Http\JsonResponse;
class MicropubResponses
{
/**
* Generate a response to be returned when the token has insufficient scope.
*
* @return JsonResponse
*/
public function insufficientScopeResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'insufficient_scope',
'error_description' => 'The tokens scope does not have the necessary requirements.',
], 401);
}
/**
* Generate a response to be returned when the token is invalid.
*
* @return JsonResponse
*/
public function invalidTokenResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
/**
* Generate a response to be returned when the token has no scope.
*
* @return JsonResponse
*/
public function tokenHasNoScopeResponse(): JsonResponse
{
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The provided token has no scopes',
], 400);
}
}

View file

@ -144,8 +144,11 @@ Route::group(['domain' => config('url.longurl')], function () {
// Micropub Endpoints // Micropub Endpoints
Route::get('api/post', 'MicropubController@get')->middleware('micropub.token'); Route::get('api/post', 'MicropubController@get')->middleware('micropub.token');
Route::post('api/post', 'MicropubController@post')->middleware('micropub.token'); Route::post('api/post', 'MicropubController@post')->middleware('micropub.token');
Route::post('api/media', 'MicropubController@media')->middleware('micropub.token', 'cors')->name('media-endpoint'); Route::get('api/media', 'MicropubMediaController@getHandler')->middleware('micropub.token');
Route::options('/api/media', 'MicropubController@mediaOptionsResponse')->middleware('cors'); Route::post('api/media', 'MicropubMediaController@media')
->middleware('micropub.token', 'cors')
->name('media-endpoint');
Route::options('/api/media', 'MicropubMediaController@mediaOptionsResponse')->middleware('cors');
// Webmention // Webmention
Route::get('webmention', 'WebMentionsController@get'); Route::get('webmention', 'WebMentionsController@get');

View file

@ -655,148 +655,6 @@ class MicropubControllerTest extends TestCase
]); ]);
} }
public function test_media_endpoint_request_with_invalid_token_return_400_response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer abc123']
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
public function test_media_endpoint_request_with_token_with_no_scope_returns_400_response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithNoScope()]
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token has no scopes']);
}
public function test_media_endpoint_request_with_insufficient_token_scopes_returns_401_response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
);
$response->assertStatus(401);
$response->assertJsonFragment(['error_description' => 'The tokens scope does not have the necessary requirements.']);
}
public function test_media_endpoint_upload_a_file()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../aaron.png';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
public function test_media_endpoint_upload_an_audio_file()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../audio.mp3';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'audio.mp3', 'audio/mpeg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
public function test_media_endpoint_upload_a_video_file()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../video.ogv';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'video.ogv', 'video/ogg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
public function test_media_endpoint_upload_a_document_file()
{
Queue::fake();
Storage::fake('s3');
$response = $this->post(
'/api/media',
[
'file' => UploadedFile::fake()->create('document.pdf', 100),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
public function test_media_endpoint_upload_an_invalid_file_return_error()
{
Queue::fake();
Storage::fake('local');
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile(
__DIR__ . '/../aaron.png',
'aaron.png',
'image/png',
UPLOAD_ERR_INI_SIZE,
true
),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson(['error_description' => 'The uploaded file failed validation']);
}
public function test_access_token_form_encoded() public function test_access_token_form_encoded()
{ {
$faker = \Faker\Factory::create(); $faker = \Faker\Factory::create();

View file

@ -0,0 +1,206 @@
<?php
namespace Tests\Feature;
use App\Jobs\ProcessMedia;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
use Tests\TestToken;
class MicropubMediaTest extends TestCase
{
use DatabaseTransactions;
use TestToken;
/** @test */
public function clientCanListLastUpload()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../aaron.png';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
$lastUploadResponse = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$lastUploadResponse->assertJson(['url' => $response->getData()->location]);
// now remove file
unlink(storage_path('app/') . $filename);
}
/** @test */
public function optionsRequestReturnsCorsResponse()
{
$response = $this->options('/api/media');
$response->assertStatus(200);
$response->assertHeader('access-control-allow-origin', '*');
}
/** @test */
public function mediaEndpointRequestWithInvalidTokenReturns400Response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer abc123']
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
/** @test */
public function mediaEndpointRequestWithTokenWithNoScopeReturns400Response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithNoScope()]
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token has no scopes']);
}
/** @test */
public function mediaEndpointRequestWithInsufficientTokenScopesReturns401Response()
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
);
$response->assertStatus(401);
$response->assertJsonFragment([
'error_description' => 'The tokens scope does not have the necessary requirements.',
]);
}
/** @test */
public function mediaEndpointUploadFile()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../aaron.png';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'aaron.png', 'image/png', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
/** @test */
public function mediaEndpointUploadAudioFile()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../audio.mp3';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'audio.mp3', 'audio/mpeg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
/** @test */
public function mediaEndpointUploadVideoFile()
{
Queue::fake();
Storage::fake('s3');
$file = __DIR__ . '/../video.ogv';
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile($file, 'video.ogv', 'video/ogg', null, true),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
/** @test */
public function mediaEndpointUploadDocumentFile()
{
Queue::fake();
Storage::fake('s3');
$response = $this->post(
'/api/media',
[
'file' => UploadedFile::fake()->create('document.pdf', 100),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$path = parse_url($response->getData()->location, PHP_URL_PATH);
$filename = substr($path, 7);
Queue::assertPushed(ProcessMedia::class);
Storage::disk('local')->assertExists($filename);
// now remove file
unlink(storage_path('app/') . $filename);
}
/** @test */
public function mediaEndpointUploadInvalidFileReturnsError()
{
Queue::fake();
Storage::fake('local');
$response = $this->post(
'/api/media',
[
'file' => new UploadedFile(
__DIR__ . '/../aaron.png',
'aaron.png',
'image/png',
UPLOAD_ERR_INI_SIZE,
true
),
],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson(['error_description' => 'The uploaded file failed validation']);
}
}