Core: Add support for file uploads for test client

Created on 5 Dec 2019  路  10Comments  路  Source: api-platform/core

Description
Add support for file uploads for test client. Current client does not seem to support this.

enhancement

Most helpful comment

All 10 comments

Please see the relevant docs section here: https://symfony.com/doc/current/components/http_client.html#uploading-data

To submit a form with file uploads, it is your responsibility to encode the body according to the multipart/form-data content-type. The Symfony Mime component makes it a few lines of code:

use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;

$formFields = [
    'regular_field' => 'some value',
    'file_field' => DataPart::fromPath('/path/to/uploaded/file'),
];
$formData = new FormDataPart($formFields);
$client->request('POST', 'https://...', [
    'headers' => $formData->getPreparedHeaders()->toArray(),
    'body' => $formData->bodyToIterable(),
]);

That being said, it could be nice to add a link to this Symfony docs entry in our own docs.

Sorry, I was mistaken on this one. Our test Client indeed does not support file uploads.

/cc @morganetouati thanks for re-discovering this problem.

Hello, is there any option to test file upload with Api-Platform? I'm using Vich and Flysystem with your guide. Thanks

I have managed to make it work using the following code:

<?php

namespace App\Tests\Controller\Api;

use App\Entity\Media;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use App\Repository\UserRepository;

/**
 * @group functional
 * @group api
 */
class MediaApiTest extends WebTestCase
{
    /**
     * Create a client with a default Authorization header.
     *
     * @param string $username
     * @param string $password
     *
     * @return \Symfony\Bundle\FrameworkBundle\Client
     */
    protected function createAuthenticatedApiClient($username = 'test')
    {
        $client = static::createClient();

        /** @var JWTTokenManagerInterface $tokenManager */
        $tokenManager = static::$kernel->getContainer()->get('lexik_jwt_authentication.jwt_manager');

        /** @var UserRepository $userRepository */
        $userRepository = static::$kernel->getContainer()->get(UserRepository::class);

        $token = $tokenManager->create($userRepository->findOneBy(['username' => $username]));

        $client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $token));

        $client->setServerParameter('HTTP_ACCEPT', 'application/ld+json');
        $client->setServerParameter('CONTENT_TYPE', 'application/ld+json');

        return $client;
    }

    public function testUserIsAbleToCreateNewMediaForAvatar()
    {
        $username = 'test';

        $client = $this->createAuthenticatedApiClient($username);

        $avatar = new UploadedFile(
            __DIR__ . '/../../../src/DataFixtures/media/test.jpg',
            'test.jpg',
            'image/jpeg',
            null,
            true
        );

        $crawler = $client->request(
            'POST', // $method
            '/api/auth/v1/media', // $url
            array( // $parameters
                'mediaType' => Media::MEDIA_TYPE_AVATAR
            ),
            array('mediaFile' => ['file' => $avatar]), // $files
            array( // $server
                'CONTENT_TYPE' => 'multipart/form-data',
            )
        );

        $this->assertSame(Response::HTTP_CREATED, $client->getResponse()->getStatusCode());
    }
}

The important parts are:

$client->setServerParameter('HTTP_ACCEPT', 'application/ld+json');
$client->setServerParameter('CONTENT_TYPE', 'application/ld+json');

And the last parameter for the request() method:

            array( // $server
                'CONTENT_TYPE' => 'multipart/form-data',
            )

@MrNicodemuz I hope you can simplify it, when my MR is in a tagged release.

$crawler = $client->request(
            'POST', // $method
            '/api/auth/v1/media', // $url
            array( // $parameters
                'mediaType' => Media::MEDIA_TYPE_AVATAR
            ),
            array('mediaFile' => ['file' => $avatar]), // $files
            array( // $server
                'CONTENT_TYPE' => 'multipart/form-data',
            )
        );

        $this->assertSame(Response::HTTP_CREATED, $client->getResponse()->getStatusCode());

Hello, thanks for your help, I am trying to implement your solution but I don't understand what is the constant MEDIA_TYPE_AVATAR should contains.
Thanks in advance for your reply :)

mediaType is just a entity field (database field) in my Media table indicating what type of media upload it is (e.g. for an user avatar)... you can omit it as it's not relevant towards file uploads...

mediaType is just a entity field (database field) in my Media table indicating what type of media upload it is (e.g. for an user avatar)... you can omit it as it's not relevant towards file uploads...

Thanks for your answer it works for me !

Was this page helpful?
0 / 5 - 0 ratings