jetruby/apollo_upload_server-ruby

v2.0.2 breaks specs using fixture_file_upload

dub-squared opened this issue ยท 4 comments

The latest version of this gem (v2.0.2) included this Pull Request that raises an error on type mismatch. This causes issues with spec that leverage the fixture_file_upload method. When using fixture_file_upload, the value argument passed to the coerce_input method is of type ActionDispatch::Http::UploadedFile not ApolloUploadServer::Wrappers::UploadedFile, so the GraphQL::CoercionError is raised. Since ApolloUploadServer::Wrappers::UploadedFile is a delegate class of ActionDispatch::Http::UploadedFile everything works fine without the type check.

Example Spec

The mutation takes a file, and returns true if the upload was successful

def query
    <<~GQL
      mutation($file: Upload!) {
        uploadCsv(file: $file)
      }
    GQL
end

let(:variables) do
  {  file: fixture_file_upload(Rails.root.join('spec', 'fixtures', 'file.csv'), 'text/csv') }
end

it 'returns a true' do
  post '/graphql', params: { query: query, variables: variables }
  expect(JSON.parse(response.body)['data']['uploadCsv']).to eq true
end

I pulled the gem down to see I could resolve the issue, but the only thing I could come up with is to remove the prior change or add an additional is_a? check for ActionDispatch::Http::UploadedFile.

Has anyone else ran into this issue? Is there a different way to define a spec to get around this?

@dub-squared yes! I have run into this issue and I'm currently trying to solve it. I've tried a few things with no luck. Please let me know if you're able to find a fix, I'll do the same ๐Ÿ™

@dub-squared I was able to fix my specs... I was completely forgetting about the format apollo-upload-client follows. https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v2.0.0-alpha.2

So following this I ended up doing:

let(:params) do
  {
    query: ...,
    variables: variables.to_json,
    operations: { "query": ..., "operationName": "...", "variables": variables }.to_json,
    map: {"1": ["variables.image"]}.to_json,
    "1": image
  }
end

and the image file ended up like:

let(:image) do
  Rack::Test::UploadedFile.new("spec/fixtures/files/image.jpg", "image/jpeg")
end

although I think you can use fixture_file_upload instead of Rack::Test::UploadedFile.

And the post would be as expected:

post graphql_path, params: params, headers: headers

after these updates I got everything working again. Let me know if this works for you! ๐Ÿ™

@sebasjimenez10 I was able to take your advice, and refactor the params structure for the example to:

let(:params) do
    {
      'operations' => {
        'query' => query,
        'variables' => { 'file' => file }
      }.to_json,
      'map' => { '1' => ['variables.file'] }.to_json,
      '1' => file
    }
 end

Once that was done, everything worked as expected. I left the fixture_file_upload since it returns a Rack::Test::UploadedFile anyway. Hopefully this helps anyone else that has a similar issue. Thanks.

Here is another working example with an array of attachments if it can help:

let(:image) { fixture_file_upload('spec/support/sample_files/logo.png') }

post '/portal_api/graphql', params: {
  operations: {
    query: query,
    variables: {
      attachments: [image]
    },
  }.to_json,
  map: { '0': ['variables.attachments.0'] }.to_json,
  '0': image
}

I checked a bit the code to understand wtf is going on under the hood and it seems that the ApolloUploadServer::Middleware middleware is "casting" the Rack::Test::UploadedFile to a ApolloUploadServer::Wrappers::UploadedFile only when the operations and map are present in the request, what is done by the apollo client (which respect the GraphQL 2 spec: https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v2.0.0-alpha.2#multipart-form-fields)