Apollo-android: Multiplatform FileUpload

Created on 1 Dec 2020  ·  7Comments  ·  Source: apollographql/apollo-android

Summary
We're trying to utilize FileUpload functionality to send images to our backend. We receive Field 'photo' of variable 'photo' has coerced Null value for NonNull type 'File!' error message every time we try to do so.

Version
2.4.5

Description
We have scalar File in our schema. As documentation states we added "File" to "com.apollographql.apollo.api.FileUpload" to custom mappings in apollo task as following:

apollo {
    customTypeMapping.set(
        mapOf(
            // other mappings
            "File" to "com.apollographql.apollo.api.FileUpload"
        )
    )
}

We have following mutation:

mutation UploadPhotoMutation($photo: File!) {
    uploadPhoto(photo: $photo)
}

and use it like this:

 val fileUpload = object : FileUpload(mimeType, Uri.fromFile(file).path!!) {
                override fun contentLength() = file.length()
                override fun fileName() = file.name
                override fun writeTo(sink: BufferedSink) {
                    sink.writeAll(file.source().buffer())
                }
            }

apolloClient.mutate(UploadPhotoMutation(fileUpload))
            .execute()
            .first()

Here is you can see operation.variables() state during OperationRequestBodyComposer.compose call. So all variables are present.

image

Bus as soon as InputFieldJsonWriter.writeCustom is called it returns GraphQLNull value for call customTypeAdapter.encode(value) and writes null to photo field.

image

And in default implementation DEFAULT_ADAPTERS for scalarTypeAdapters to which defaults scalarTypeAdapters.adapterFor(scalarType) call in this case has following code for FileUpload:

 mapOf("com.apollographql.apollo.api.FileUpload" to object : CustomTypeAdapter<FileUpload> {
          override fun decode(value: CustomTypeValue<*>): FileUpload {
            // TODO: is there a valid use case for decoding a FileUpload or should we throw here?
            return FileUpload("", value.value?.toString() ?: "")
          }

          override fun encode(value: FileUpload): CustomTypeValue<*> {
            return GraphQLNull
          }
        })

So call to encode will always return GraphQLNull and field photo is mapped to null instead of real value

Feature

All 7 comments

Although worth to mention that we use it in multiplatform module.

After further investigation it seems that problem is json returned from composeRequestBody(scalarTypeAdapters: ScalarTypeAdapters) call in generated UploadPhotoMutation.
It returns {“operationName”:“UploadPhotoMutation”,“variables”:{“photo”:null},“query”:“mutation UploadPhotoMutation($photo: File!) { uploadPhoto(photo: $photo) }“}

But it also should contain map="{\"photo\": [\"variables.photo\"]}" and photo=@"/path/to/image/image.jpg" as formdata parameters

Multiplatform doesn't have support for FileUpload yet. FileUpload is working with reflection on the JVM (see #2570) so it'll have to be implemented differently in multiplatform.

Oh, I see, thanks. Btw is FileUpload is implemented on iOS? If so we could switch to native versions instead of multiplatform

thank you so much!

I also recommend reading this caveat right above what Martin linked 😇 - that also might be a way to think about file uploads on multiplatform that could be easier to achieve.

Was this page helpful?
0 / 5 - 0 ratings