Search code examples
symfonygraphqlphpunitapi-platform.com

How can I Unit Test GraphQL file Upload with API Platform?


In addition to my other tests against my GraphQL API Platform backend, I am attempting to test file uploads. I'm not quite sure whether the ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client class has the ability to facilitate this test, or if Symfony\Component\HttpFoundation\File\UploadedFile should be used to provide the test file as it is for a REST operation.

Here is roughly where I am at in terms of putting together this test. (With some irrelevant parts removed for simplification)

 public function testCreateMediaObject(): void {
    $file = new UploadedFile('fixtures/files/logo.png', 'logo.png');
    $client = self::createClient();

    $gql = <<<GQL
    mutation UploadMediaObject(\$file: Upload!) {
      uploadMediaObject(input: {file: \$file}) {
        mediaObject {
          id
          contentUrl
        }
      }
    }
    GQL;

    $response = $client->request('POST', '/api/graphql', [
      'headers' => ['Content-Type' => 'application/json'],
      'json' => [
        'query' => $gql,
        'variables' => ["file" => null],
        'map' => ['0' => ['variables.file']],
        '0' => $file,
      ]
    ]);
    dd($response);

  }

The response I get seems to indicate that the file isn't being included as expected. Something like...

Variable "$file" of non-null type "Upload!" must not be null.

Or.. if I try to attach the file by simply assigning it directly in the variables property...

    $response = $client->request('POST', '/api/graphql', [
      'headers' => ['Content-Type' => 'application/json'],
      'json' => [
        'query' => $gql,
        'variables' => ["file" => $file],
      ]
    ]);

then...

Variable "$file" got invalid value []; Expected type Upload; Could not get uploaded file, be sure to conform to GraphQL multipart request specification. Instead got: []

In my most recent attempt, I changed things up quite a bit after sifting through the graphql code...

    $formFields = [
      'operations' => '{ "query": "mutation ($file: Upload!) { uploadMediaObject(input: {file: $file}) { mediaObject { id contentUrl } } }", "variables": { "file": null } }',
      'map' => "{'0': ['variables.file']}",
      '0' => DataPart::fromPath(__DIR__.'/../../fixtures/files/logo.png'),
    ];
    $formData = new FormDataPart($formFields);
    $response = $client->request('POST', '/api/graphql', [
      'headers' => $formData->getPreparedHeaders()->toArray(),
      'body' => $formData->bodyToString(),
    ]);

The problem with this last attempt is that the server isn't seeing any body parameters. So I receiving the exception

'GraphQL multipart request does not respect the specification.'

Which is found in /api-platform/core/src/GraphQl/Action/EntrypointAction.php within the parseMultipartRequest method.


Solution

  • After a few hours of debugging I found this solution:

    $formData = new FormDataPart();
    $file = new UploadedFile('src/DataFixtures/files/test.txt', 'test.txt');
        
    $response = $this->$client->request('POST', 'api/graphql', [
        'headers' => $formData->getPreparedHeaders()->toArray(),
        'extra' => [
            'parameters' => [
                'operations' => '{ "query": "mutation UploadMediaObject($file: Upload!) { uploadMediaObject(input: {file: $file}) { mediaObject { id contentUrl } } }", "variables": { "file": null } }',
                'map' => '{ "0": ["variables.file"] }',
                '0' => @$file,
            ],
            'files' => [
                $file,
            ],
        ],
    ]);
    

    Refrence: