From 288ba634c85390fb6cf882455ee0d15dc2c2ce16 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 9 Sep 2020 11:22:33 +0200 Subject: [PATCH 1/3] Allow to encode custom scalar types as null and use it for file uploads --- .../apollographql/apollo/api/CustomTypeValue.kt | 3 +++ .../apollographql/apollo/api/ScalarTypeAdapters.kt | 3 ++- .../api/internal/json/InputFieldJsonWriter.kt | 2 ++ .../api/internal/json/InputFieldJsonWriterTest.kt | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomTypeValue.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomTypeValue.kt index ff3c4e7405a..e815ece87a0 100644 --- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomTypeValue.kt +++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomTypeValue.kt @@ -9,6 +9,8 @@ import kotlin.jvm.JvmStatic **/ sealed class CustomTypeValue(@JvmField val value: T) { + object GraphQLNull: CustomTypeValue(Unit) + /** * Represents a `String` value */ @@ -91,6 +93,7 @@ sealed class CustomTypeValue(@JvmField val value: T) { companion object { + // TODO: should this method accept nullable values in case a scalar type is represented as 'null'? @JvmStatic @Suppress("UNCHECKED_CAST") fun fromRawValue(value: Any): CustomTypeValue<*> { diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/ScalarTypeAdapters.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/ScalarTypeAdapters.kt index 20978b6be29..f74d005dd1e 100644 --- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/ScalarTypeAdapters.kt +++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/ScalarTypeAdapters.kt @@ -84,11 +84,12 @@ class ScalarTypeAdapters(val customAdapters: Map { 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 GraphQLString(value.mimetype) + return GraphQLNull } }) + createDefaultScalarTypeAdapter("java.util.Map", "kotlin.collections.Map") { value -> diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriter.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriter.kt index e2d56bbf255..045f8fab6d2 100644 --- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriter.kt +++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriter.kt @@ -80,6 +80,7 @@ class InputFieldJsonWriter( is GraphQLString -> writeString(fieldName, customTypeValue.value) is GraphQLBoolean -> writeBoolean(fieldName, customTypeValue.value) is GraphQLNumber -> writeNumber(fieldName, customTypeValue.value) + is GraphQLNull -> writeString(fieldName, null) is GraphQLJsonObject -> jsonWriter.name(fieldName).apply { writeToJson(customTypeValue.value, this) } is GraphQLJsonList -> jsonWriter.name(fieldName).apply { writeToJson(customTypeValue.value, this) } } @@ -195,6 +196,7 @@ class InputFieldJsonWriter( is GraphQLNumber -> writeNumber(customTypeValue.value) is GraphQLJsonObject -> writeToJson(customTypeValue.value, jsonWriter) is GraphQLJsonList -> writeToJson(customTypeValue.value, jsonWriter) + is GraphQLNull -> writeString(null) } } diff --git a/apollo-api/src/commonTest/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriterTest.kt b/apollo-api/src/commonTest/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriterTest.kt index f85fcd7ceda..b96b98e4f90 100644 --- a/apollo-api/src/commonTest/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriterTest.kt +++ b/apollo-api/src/commonTest/kotlin/com/apollographql/apollo/api/internal/json/InputFieldJsonWriterTest.kt @@ -130,6 +130,20 @@ class InputFieldJsonWriterTest { assertEquals("{\"someField\":\"someValue\",\"someNullField\":null", jsonBuffer.readUtf8()) } + @Test + fun writeCustomNull() { + val customTypeAdapters: MutableMap> = HashMap() + val scalarType = MockCustomScalarType(CustomTypeValue.GraphQLNumber::class, "com.apollographql.apollo.api.CustomTypeValue.GraphQLNull") + customTypeAdapters[scalarType] = object : MockCustomTypeAdapter() { + override fun encode(value: Any?): CustomTypeValue<*> { + return CustomTypeValue.GraphQLNull + } + } + val inputFieldJsonWriter = InputFieldJsonWriter(jsonWriter, ScalarTypeAdapters(customTypeAdapters)) + inputFieldJsonWriter.writeCustom("someField", scalarType, null) + assertEquals("{\"someField\":null", jsonBuffer.readUtf8()) + } + @Test fun writeCustomJsonObject() { val value = mapOf( From 42806a1f6b96095675e490cfed592676bf179486 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 10 Sep 2020 16:32:09 +0200 Subject: [PATCH 2/3] fix integration tests --- .../expectedOperationsPartBodyMultiple.json | 2 +- .../expectedOperationsPartBodyNested.json | 2 +- .../expectedOperationsPartBodySingle.json | 2 +- .../expectedOperationsPartBodyTwice.json | 2 +- .../ApolloServerInterceptorTest.java | 237 ------------------ .../ApolloServerInterceptorTest.kt | 208 +++++++++++++++ 6 files changed, 212 insertions(+), 241 deletions(-) delete mode 100644 apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.java create mode 100644 apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.kt diff --git a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyMultiple.json b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyMultiple.json index 1863a1b9295..faccafcd7df 100644 --- a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyMultiple.json +++ b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyMultiple.json @@ -1 +1 @@ -{"operationName":"MultipleUpload","variables":{"files":["image/jpg","image/png"]},"query":"mutation MultipleUpload($files: [Upload!]!) { multipleUpload(files: $files) { __typename id path filename mimetype } }"} +{"operationName":"MultipleUpload","variables":{"files":[null,null]},"query":"mutation MultipleUpload($files: [Upload!]!) { multipleUpload(files: $files) { __typename id path filename mimetype } }"} diff --git a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyNested.json b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyNested.json index 7f5db9e49eb..7040db50a68 100644 --- a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyNested.json +++ b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyNested.json @@ -1 +1 @@ -{"operationName":"NestedUpload","variables":{"topFile":"image/png","topFileList":["image/jpg","plain/txt"],"nested":{"recursiveNested":[{"file":"plain/txt","fileList":["image/jpg","image/png"]},{"file":"image/jpg","fileList":["plain/txt","image/png"]}],"file":"image/png","fileList":["plain/txt","image/jpg"]}},"query":"mutation NestedUpload($topFile: Upload, $topFileList: [Upload], $nested: NestedObject) { nestedUpload(topFile: $topFile, topFileList: $topFileList, nested: $nested) }"} +{"operationName":"NestedUpload","variables":{"topFile":null,"topFileList":[null,null],"nested":{"recursiveNested":[{"file":null,"fileList":[null,null]},{"file":null,"fileList":[null,null]}],"file":null,"fileList":[null,null]}},"query":"mutation NestedUpload($topFile: Upload, $topFileList: [Upload], $nested: NestedObject) { nestedUpload(topFile: $topFile, topFileList: $topFileList, nested: $nested) }"} diff --git a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodySingle.json b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodySingle.json index c32506dfc58..400620946ca 100644 --- a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodySingle.json +++ b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodySingle.json @@ -1 +1 @@ -{"operationName":"SingleUpload","variables":{"file":"image/jpg"},"query":"mutation SingleUpload($file: Upload!) { singleUpload(file: $file) { __typename id path filename mimetype } }"} +{"operationName":"SingleUpload","variables":{"file":null},"query":"mutation SingleUpload($file: Upload!) { singleUpload(file: $file) { __typename id path filename mimetype } }"} diff --git a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyTwice.json b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyTwice.json index 55c42813907..22eb1c5c178 100644 --- a/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyTwice.json +++ b/apollo-integration/src/test/fixtures/ApolloServerInterceptorFileUploadTest/expectedOperationsPartBodyTwice.json @@ -1 +1 @@ -{"operationName":"SingleUploadTwice","variables":{"file1":"image/jpg","file2":"image/png"},"query":"mutation SingleUploadTwice($file1: Upload!, $file2: Upload!) { file1: singleUpload(file: $file1) { __typename id path filename mimetype } file2: singleUpload(file: $file2) { __typename id path filename mimetype } }"} +{"operationName":"SingleUploadTwice","variables":{"file1":null,"file2":null},"query":"mutation SingleUploadTwice($file1: Upload!, $file2: Upload!) { file1: singleUpload(file: $file1) { __typename id path filename mimetype } file2: singleUpload(file: $file2) { __typename id path filename mimetype } }"} diff --git a/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.java b/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.java deleted file mode 100644 index a967494204a..00000000000 --- a/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.apollographql.apollo.internal.interceptor; - -import com.apollographql.apollo.Logger; -import com.apollographql.apollo.Utils; -import com.apollographql.apollo.api.CustomTypeAdapter; -import com.apollographql.apollo.api.Input; -import com.apollographql.apollo.api.ScalarType; -import com.apollographql.apollo.api.ScalarTypeAdapters; -import com.apollographql.apollo.api.cache.http.HttpCache; -import com.apollographql.apollo.api.cache.http.HttpCachePolicy; -import com.apollographql.apollo.api.internal.ApolloLogger; -import com.apollographql.apollo.api.internal.Optional; -import com.apollographql.apollo.cache.ApolloCacheHeaders; -import com.apollographql.apollo.cache.CacheHeaders; -import com.apollographql.apollo.integration.interceptor.AllFilmsQuery; -import com.apollographql.apollo.request.RequestHeaders; -import com.google.common.base.Predicate; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.HttpUrl; -import okhttp3.Request; -import okhttp3.Response; -import okio.Buffer; -import okio.Timeout; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.Test; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.fail; - -public class ApolloServerInterceptorTest { - private final HttpUrl serverUrl = HttpUrl.parse("http://google.com"); - private final AllFilmsQuery query = AllFilmsQuery.builder() - .after("some cursor") - .beforeInput(Input.absent()) - .firstInput(Input.fromNullable(null)) - .last(100) - .build(); - - @Test public void testDefaultHttpCall() throws Exception { - Predicate requestAssertPredicate = new Predicate() { - @Override public boolean apply(@Nullable Request request) { - assertThat(request).isNotNull(); - assertDefaultRequestHeaders(request); - assertThat(request.url()).isEqualTo(serverUrl); - assertThat(request.method()).isEqualTo("POST"); - assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE); - assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull(); - assertRequestBody(request); - return true; - } - }; - - ApolloServerInterceptor interceptor = new ApolloServerInterceptor(serverUrl, - new AssertHttpCallFactory(requestAssertPredicate), null, false, - new ScalarTypeAdapters(Collections.>emptyMap()), - new ApolloLogger(null)); - - interceptor.httpPostCall(query, CacheHeaders.NONE, RequestHeaders.NONE, true, false); - } - - @Test public void testCachedHttpCall() throws Exception { - ScalarTypeAdapters scalarTypeAdapters = - new ScalarTypeAdapters(Collections.>emptyMap()); - final String cacheKey = ApolloServerInterceptor.cacheKey(query, scalarTypeAdapters); - Predicate requestAssertPredicate = new Predicate() { - @Override public boolean apply(@Nullable Request request) { - assertThat(request).isNotNull(); - assertDefaultRequestHeaders(request); - assertThat(request.url()).isEqualTo(serverUrl); - assertThat(request.method()).isEqualTo("POST"); - assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE); - assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isEqualTo(cacheKey); - assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isEqualTo("NETWORK_FIRST"); - assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isEqualTo("10000"); - assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isEqualTo("false"); - assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isEqualTo("false"); - assertThat(request.header(HttpCache.CACHE_DO_NOT_STORE)).isEqualTo("true"); - assertRequestBody(request); - return true; - } - }; - - ApolloServerInterceptor interceptor = new ApolloServerInterceptor(serverUrl, - new AssertHttpCallFactory(requestAssertPredicate), - HttpCachePolicy.NETWORK_FIRST.expireAfter(10, TimeUnit.SECONDS), false, - scalarTypeAdapters, new ApolloLogger(null)); - - interceptor.httpPostCall(query, CacheHeaders.builder().addHeader(ApolloCacheHeaders.DO_NOT_STORE, "true").build(), - RequestHeaders.NONE, true, false); - } - - @Test public void testAdditionalHeaders() throws Exception { - final String testHeader1 = "TEST_HEADER_1"; - final String testHeaderValue1 = "crappy_value"; - final String testHeader2 = "TEST_HEADER_2"; - final String testHeaderValue2 = "fantastic_value"; - final String testHeader3 = "TEST_HEADER_3"; - final String testHeaderValue3 = "awesome_value"; - - Predicate requestAssertPredicate = new Predicate() { - @Override public boolean apply(@Nullable Request request) { - assertThat(request).isNotNull(); - assertDefaultRequestHeaders(request); - assertThat(request.url()).isEqualTo(serverUrl); - assertThat(request.method()).isEqualTo("POST"); - assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE); - assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull(); - assertThat(request.header(testHeader1)).isEqualTo(testHeaderValue1); - assertThat(request.header(testHeader2)).isEqualTo(testHeaderValue2); - assertThat(request.header(testHeader3)).isEqualTo(testHeaderValue3); - assertRequestBody(request); - return true; - } - }; - - RequestHeaders requestHeaders = RequestHeaders.builder() - .addHeader(testHeader1, testHeaderValue1) - .addHeader(testHeader2, testHeaderValue2) - .addHeader(testHeader3, testHeaderValue3) - .build(); - - ApolloServerInterceptor interceptor = new ApolloServerInterceptor(serverUrl, - new AssertHttpCallFactory(requestAssertPredicate), null, false, - new ScalarTypeAdapters(Collections.>emptyMap()), - new ApolloLogger(null)); - - interceptor.httpPostCall(query, CacheHeaders.NONE, requestHeaders, true, false); - } - - @Test public void testUseHttpGetForQueries() throws IOException { - Predicate requestAssertPredicate = new Predicate() { - @Override public boolean apply(@Nullable Request request) { - assertThat(request).isNotNull(); - assertDefaultRequestHeaders(request); - assertThat(request.method()).isEqualTo("GET"); - assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isNull(); - assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull(); - assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull(); - assertThat(request.url().queryParameter("query")).isEqualTo(query.queryDocument().replace("\n", "")); - assertThat(request.url().queryParameter("operationName")).isEqualTo(query.name().name()); - assertThat(request.url().queryParameter("variables")).isEqualTo("{\"after\":\"some cursor\",\"first\":null,\"last\":100}"); - assertThat(request.url().queryParameter("extensions")).isEqualTo("{\"persistedQuery\":{\"version\":1," + - "\"sha256Hash\":\"" + query.operationId() + "\"}}"); - return true; - } - }; - - ApolloServerInterceptor interceptor = new ApolloServerInterceptor(serverUrl, - new AssertHttpCallFactory(requestAssertPredicate), null, false, - new ScalarTypeAdapters(Collections.>emptyMap()), - new ApolloLogger(null)); - - interceptor.httpGetCall(query, CacheHeaders.NONE, RequestHeaders.NONE, true, true); - } - - private void assertDefaultRequestHeaders(Request request) { - assertThat(request.header(ApolloServerInterceptor.HEADER_ACCEPT_TYPE)).isEqualTo(ApolloServerInterceptor.ACCEPT_TYPE); - assertThat(request.header(ApolloServerInterceptor.HEADER_APOLLO_OPERATION_ID)).isEqualTo(query.operationId()); - assertThat(request.header(ApolloServerInterceptor.HEADER_APOLLO_OPERATION_NAME)).isEqualTo(query.name().name()); - assertThat(request.tag()).isEqualTo(query.operationId()); - } - - private void assertRequestBody(Request request) { - assertThat(request.body().contentType()).isEqualTo(ApolloServerInterceptor.MEDIA_TYPE); - Buffer bodyBuffer = new Buffer(); - try { - request.body().writeTo(bodyBuffer); - } catch (Exception e) { - throw new RuntimeException(e); - } - Utils.INSTANCE.checkTestFixture(bodyBuffer.readUtf8(), "ApolloServerInterceptorTest/interceptorRequestBody.json"); - } - - private static class AssertHttpCallFactory implements Call.Factory { - final Predicate predicate; - - AssertHttpCallFactory(Predicate predicate) { - this.predicate = predicate; - } - - @Override public Call newCall(@NotNull Request request) { - if (!predicate.apply(request)) { - fail("Assertion failed"); - } - return new NoOpCall(); - } - } - - private static class NoOpCall implements Call { - @Override public Request request() { - return null; - } - - @Override public Response execute() { - return null; - } - - @Override public void enqueue(Callback responseCallback) { - } - - @Override public void cancel() { - } - - @Override public boolean isExecuted() { - return false; - } - - @Override public boolean isCanceled() { - return false; - } - - @Override public Call clone() { - return null; - } - - @Override public Timeout timeout() { - return null; - } - } -} diff --git a/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.kt b/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.kt new file mode 100644 index 00000000000..1aaeb99585e --- /dev/null +++ b/apollo-integration/src/test/java/com/apollographql/apollo/internal/interceptor/ApolloServerInterceptorTest.kt @@ -0,0 +1,208 @@ +package com.apollographql.apollo.internal.interceptor + +import com.apollographql.apollo.Logger +import com.apollographql.apollo.Utils.checkTestFixture +import com.apollographql.apollo.api.CustomTypeAdapter +import com.apollographql.apollo.api.Input +import com.apollographql.apollo.api.ScalarType +import com.apollographql.apollo.api.ScalarTypeAdapters +import com.apollographql.apollo.api.cache.http.HttpCache +import com.apollographql.apollo.api.cache.http.HttpCachePolicy +import com.apollographql.apollo.api.internal.ApolloLogger +import com.apollographql.apollo.api.internal.Optional +import com.apollographql.apollo.cache.ApolloCacheHeaders +import com.apollographql.apollo.cache.CacheHeaders +import com.apollographql.apollo.integration.interceptor.AllFilmsQuery +import com.apollographql.apollo.request.RequestHeaders +import com.google.common.base.Predicate +import com.google.common.truth.Truth +import junit.framework.Assert +import okhttp3.* +import okio.Buffer +import okio.Timeout +import org.junit.Test +import java.io.IOException +import java.lang.UnsupportedOperationException +import java.util.concurrent.TimeUnit + +class ApolloServerInterceptorTest { + private val serverUrl = HttpUrl.parse("http://google.com")!! + private val query = AllFilmsQuery.builder() + .after("some cursor") + .beforeInput(Input.absent()) + .firstInput(Input.fromNullable(null)) + .last(100) + .build() + + @Test + @Throws(Exception::class) + fun testDefaultHttpCall() { + val requestAssertPredicate = Predicate { request -> + Truth.assertThat(request).isNotNull() + assertDefaultRequestHeaders(request) + Truth.assertThat(request!!.url()).isEqualTo(serverUrl) + Truth.assertThat(request.method()).isEqualTo("POST") + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE) + Truth.assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull() + assertRequestBody(request) + true + } + val interceptor = ApolloServerInterceptor(serverUrl, + AssertHttpCallFactory(requestAssertPredicate), null, false, + ScalarTypeAdapters(emptyMap>()), + ApolloLogger(null)) + interceptor.httpPostCall(query, CacheHeaders.NONE, RequestHeaders.NONE, true, false) + } + + @Test + @Throws(Exception::class) + fun testCachedHttpCall() { + val scalarTypeAdapters = ScalarTypeAdapters(emptyMap>()) + val cacheKey: String = ApolloServerInterceptor.cacheKey(query, scalarTypeAdapters) + val requestAssertPredicate = Predicate { request -> + Truth.assertThat(request).isNotNull() + assertDefaultRequestHeaders(request) + Truth.assertThat(request!!.url()).isEqualTo(serverUrl) + Truth.assertThat(request.method()).isEqualTo("POST") + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE) + Truth.assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isEqualTo(cacheKey) + Truth.assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isEqualTo("NETWORK_FIRST") + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isEqualTo("10000") + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isEqualTo("false") + Truth.assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isEqualTo("false") + Truth.assertThat(request.header(HttpCache.CACHE_DO_NOT_STORE)).isEqualTo("true") + assertRequestBody(request) + true + } + val interceptor = ApolloServerInterceptor(serverUrl, + AssertHttpCallFactory(requestAssertPredicate), + HttpCachePolicy.NETWORK_FIRST.expireAfter(10, TimeUnit.SECONDS), false, + scalarTypeAdapters, ApolloLogger(null)) + interceptor.httpPostCall(query, CacheHeaders.builder().addHeader(ApolloCacheHeaders.DO_NOT_STORE, "true").build(), + RequestHeaders.NONE, true, false) + } + + @Test + @Throws(Exception::class) + fun testAdditionalHeaders() { + val testHeader1 = "TEST_HEADER_1" + val testHeaderValue1 = "crappy_value" + val testHeader2 = "TEST_HEADER_2" + val testHeaderValue2 = "fantastic_value" + val testHeader3 = "TEST_HEADER_3" + val testHeaderValue3 = "awesome_value" + val requestAssertPredicate = Predicate { request -> + Truth.assertThat(request).isNotNull() + assertDefaultRequestHeaders(request) + Truth.assertThat(request!!.url()).isEqualTo(serverUrl) + Truth.assertThat(request.method()).isEqualTo("POST") + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isEqualTo(ApolloServerInterceptor.CONTENT_TYPE) + Truth.assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull() + Truth.assertThat(request.header(testHeader1)).isEqualTo(testHeaderValue1) + Truth.assertThat(request.header(testHeader2)).isEqualTo(testHeaderValue2) + Truth.assertThat(request.header(testHeader3)).isEqualTo(testHeaderValue3) + assertRequestBody(request) + true + } + val requestHeaders: RequestHeaders = RequestHeaders.builder() + .addHeader(testHeader1, testHeaderValue1) + .addHeader(testHeader2, testHeaderValue2) + .addHeader(testHeader3, testHeaderValue3) + .build() + val interceptor = ApolloServerInterceptor(serverUrl, + AssertHttpCallFactory(requestAssertPredicate), null, false, + ScalarTypeAdapters(emptyMap>()), + ApolloLogger(null)) + interceptor.httpPostCall(query, CacheHeaders.NONE, requestHeaders, true, false) + } + + @Test + @Throws(IOException::class) + fun testUseHttpGetForQueries() { + val requestAssertPredicate = Predicate { request -> + Truth.assertThat(request).isNotNull() + assertDefaultRequestHeaders(request) + Truth.assertThat(request!!.method()).isEqualTo("GET") + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_CONTENT_TYPE)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_KEY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_FETCH_STRATEGY_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_TIMEOUT_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_EXPIRE_AFTER_READ_HEADER)).isNull() + Truth.assertThat(request.header(HttpCache.CACHE_PREFETCH_HEADER)).isNull() + Truth.assertThat(request.url().queryParameter("query")).isEqualTo(query.queryDocument().replace("\n", "")) + Truth.assertThat(request.url().queryParameter("operationName")).isEqualTo(query.name().name()) + Truth.assertThat(request.url().queryParameter("variables")).isEqualTo("{\"after\":\"some cursor\",\"first\":null,\"last\":100}") + Truth.assertThat(request.url().queryParameter("extensions")).isEqualTo("{\"persistedQuery\":{\"version\":1," + + "\"sha256Hash\":\"" + query.operationId() + "\"}}") + true + } + val interceptor = ApolloServerInterceptor(serverUrl, + AssertHttpCallFactory(requestAssertPredicate), null, false, + ScalarTypeAdapters(emptyMap>()), + ApolloLogger(null)) + interceptor.httpGetCall(query, CacheHeaders.NONE, RequestHeaders.NONE, true, true) + } + + private fun assertDefaultRequestHeaders(request: Request?) { + Truth.assertThat(request!!.header(ApolloServerInterceptor.HEADER_ACCEPT_TYPE)).isEqualTo(ApolloServerInterceptor.ACCEPT_TYPE) + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_APOLLO_OPERATION_ID)).isEqualTo(query.operationId()) + Truth.assertThat(request.header(ApolloServerInterceptor.HEADER_APOLLO_OPERATION_NAME)).isEqualTo(query.name().name()) + Truth.assertThat(request.tag()).isEqualTo(query.operationId()) + } + + private fun assertRequestBody(request: Request?) { + Truth.assertThat(request!!.body()!!.contentType()).isEqualTo(ApolloServerInterceptor.MEDIA_TYPE) + val bodyBuffer = Buffer() + try { + request.body()!!.writeTo(bodyBuffer) + } catch (e: Exception) { + throw RuntimeException(e) + } + checkTestFixture(bodyBuffer.readUtf8(), "ApolloServerInterceptorTest/interceptorRequestBody.json") + } + + private class AssertHttpCallFactory internal constructor(val predicate: Predicate) : Call.Factory { + override fun newCall(request: Request): Call { + if (!predicate.apply(request)) { + Assert.fail("Assertion failed") + } + return NoOpCall() + } + } + + private class NoOpCall : Call { + override fun request(): Request { + throw UnsupportedOperationException() + } + + override fun execute(): Response { + throw UnsupportedOperationException() + } + + override fun enqueue(responseCallback: Callback) {} + override fun cancel() {} + override fun isExecuted(): Boolean { + return false + } + + override fun isCanceled(): Boolean { + return false + } + + override fun clone(): Call { + throw UnsupportedOperationException() + } + + override fun timeout(): Timeout { + throw UnsupportedOperationException() + } + } +} \ No newline at end of file From 089e358671702fced5c820f53037577fcf0e533d Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 10 Sep 2020 17:26:10 +0200 Subject: [PATCH 3/3] update metalava --- apollo-api/api.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apollo-api/api.txt b/apollo-api/api.txt index 49e56c70533..bedf373ec16 100644 --- a/apollo-api/api.txt +++ b/apollo-api/api.txt @@ -115,6 +115,10 @@ package com.apollographql.apollo.api { method public error.NonExistentClass hashCode(); } + public static final class CustomTypeValue.GraphQLNull extends com.apollographql.apollo.api.CustomTypeValue { + field public static final com.apollographql.apollo.api.CustomTypeValue.GraphQLNull INSTANCE; + } + public static final class CustomTypeValue.GraphQLNumber extends com.apollographql.apollo.api.CustomTypeValue { ctor public CustomTypeValue.GraphQLNumber(error.NonExistentClass); method public error.NonExistentClass equals(error.NonExistentClass);