package com.apollographql.apollo.internal.reader; import com.apollographql.apollo.CustomTypeAdapter; import com.apollographql.apollo.api.Field; import com.apollographql.apollo.api.Operation; import com.apollographql.apollo.api.OperationName; import com.apollographql.apollo.api.ResponseFieldMapper; import com.apollographql.apollo.api.ResponseReader; import com.apollographql.apollo.api.ScalarType; import com.apollographql.apollo.api.internal.Optional; import com.apollographql.apollo.cache.normalized.CacheKey; import com.apollographql.apollo.cache.normalized.Record; import com.apollographql.apollo.internal.cache.normalized.ResponseNormalizer; import com.apollographql.apollo.internal.field.MapFieldValueResolver; import org.junit.Test; import java.io.IOException; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static org.junit.Assert.fail; public class ResponseReaderTest { @Test public void readString() throws Exception { Field successField = Field.forString("successFieldResponseName", "successFieldName", null, false); Field classCastExceptionField = Field.forString("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", "response1"); recordSet.put("successFieldName", "response2"); recordSet.put("classCastExceptionFieldResponseName", 1); checkReadFields(recordSet, successField, classCastExceptionField, "response1"); } @Test public void readInt() throws Exception { Field successField = Field.forInt("successFieldResponseName", "successFieldName", null, false); Field classCastExceptionField = Field.forInt("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", BigDecimal.valueOf(1)); recordSet.put("successFieldName", BigDecimal.valueOf(2)); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, 1); } @Test public void readLong() throws Exception { Field successField = Field.forLong("successFieldResponseName", "successFieldName", null, false); Field classCastExceptionField = Field.forLong("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", BigDecimal.valueOf(1)); recordSet.put("successFieldName", BigDecimal.valueOf(2)); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, 1); } @Test public void readDouble() throws Exception { Field successField = Field.forDouble("successFieldResponseName", "successFieldName", null, false); Field classCastExceptionField = Field.forDouble("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", BigDecimal.valueOf(1.1)); recordSet.put("successFieldName", BigDecimal.valueOf(2.2)); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, 1.1D); } @Test public void readBoolean() throws Exception { Field successField = Field.forBoolean("successFieldResponseName", "successFieldName", null, false); Field classCastExceptionField = Field.forBoolean("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", true); recordSet.put("successFieldName", false); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, true); } @Test public void readObject() throws Exception { final Object responseObject1 = new Object(); final Object responseObject2 = new Object(); final Field.ObjectReader objectReader = new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return responseObject1; } }; Field successField = Field.forObject("successFieldResponseName", "successFieldName", null, false, objectReader); Field classCastExceptionField = Field.forObject("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false, new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return reader.read(Field.forString("anything", "anything", null, true)); } }); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", responseObject1); recordSet.put("successFieldName", responseObject2); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, responseObject1); } @Test public void readScalarList() throws Exception { final Field.ListReader listReader = new Field.ListReader<String>() { @Override public String read(Field.ListItemReader reader) throws IOException { return reader.readString(); } }; Field successField = Field.forList("successFieldResponseName", "successFieldName", null, false, listReader); Field classCastExceptionField = Field.forList("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false, listReader); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", asList("value1", "value2", "value3")); recordSet.put("successFieldName", asList("value4", "value5")); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, asList("value1", "value2", "value3")); } @Test public void readObjectList() throws Exception { final Object responseObject1 = new Object(); final Object responseObject2 = new Object(); final Field.ObjectReader objectReader = new Field.ObjectReader() { @Override public Object read(ResponseReader reader) throws IOException { return responseObject1; } }; Field successField = Field.forList("successFieldResponseName", "successFieldName", null, false, objectReader); Field classCastExceptionField = Field.forList("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false, objectReader); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", asList(responseObject1)); recordSet.put("successFieldName", asList(responseObject2)); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, asList(responseObject1)); } @Test public void readCustom() throws Exception { Field successField = Field.forCustomType("successFieldResponseName", "successFieldName", null, false, CUSTOM_TYPE); Field classCastExceptionField = Field.forCustomType("classCastExceptionFieldResponseName", "classCastExceptionFieldName", null, false, CUSTOM_TYPE); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", "2017-04-16"); recordSet.put("successFieldName", "2018-04-16"); recordSet.put("classCastExceptionFieldResponseName", "anything"); checkReadFields(recordSet, successField, classCastExceptionField, DATE_TIME_FORMAT.parse("2017-04-16") ); } @Test public void readConditional() throws Exception { final Object responseObject1 = new Object(); final Object responseObject2 = new Object(); Field.ConditionalTypeReader conditionalTypeReader = new Field.ConditionalTypeReader() { @Override public Object read(String conditionalType, ResponseReader reader) throws IOException { if (conditionalType.equals("responseObject1")) { return responseObject1; } else { return responseObject2; } } }; Field successField = Field.forConditionalType("successFieldResponseName", "successFieldName", conditionalTypeReader); Field classCastExceptionField = Field.forConditionalType("classCastExceptionFieldResponseName", "classCastExceptionFieldName", conditionalTypeReader); Map<String, Object> recordSet = new HashMap<>(); recordSet.put("successFieldResponseName", "responseObject1"); recordSet.put("successFieldName", "responseObject2"); recordSet.put("classCastExceptionFieldResponseName", 1); checkReadFields(recordSet, successField, classCastExceptionField, responseObject1); } @Test public void optionalFieldsIOException() throws Exception { List<Field> fields = asList( Field.forString("stringField", "stringField", null, true), Field.forInt("intField", "intField", null, true), Field.forLong("longField", "longField", null, true), Field.forDouble("doubleField", "doubleField", null, true), Field.forBoolean("booleanField", "booleanField", null, true), Field.forObject("objectField", "objectField", null, true, new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return new Object(); } }), Field.forList("scalarListField", "scalarListField", null, true, new Field.ListReader<String>() { @Override public String read(Field.ListItemReader reader) throws IOException { return reader.readString(); } }), Field.forList("objectListField", "objectListField", null, true, new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return new Object(); } }), Field.forCustomType("customTypeField", "customTypeField", null, true, CUSTOM_TYPE) ); RealResponseReader<Map<String, Object>> responseReader = responseReader(Collections.<String, Object>emptyMap()); for (Field field : fields) { try { responseReader.read(field); fail("expected IOException for field: " + field); } catch (IOException expected) { //expected } } } @Test public void mandatoryFieldsIOException() throws Exception { List<Field> fields = asList( Field.forString("stringField", "stringField", null, false), Field.forInt("intField", "intField", null, false), Field.forLong("longField", "longField", null, false), Field.forDouble("doubleField", "doubleField", null, false), Field.forBoolean("booleanField", "booleanField", null, false), Field.forObject("objectField", "objectField", null, false, new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return new Object(); } }), Field.forList("scalarListField", "scalarListField", null, false, new Field.ListReader<String>() { @Override public String read(Field.ListItemReader reader) throws IOException { return reader.readString(); } }), Field.forList("objectListField", "objectListField", null, false, new Field.ObjectReader<Object>() { @Override public Object read(ResponseReader reader) throws IOException { return new Object(); } }), Field.forCustomType("customTypeField", "customTypeField", null, false, CUSTOM_TYPE) ); RealResponseReader<Map<String, Object>> responseReader = responseReader(Collections.<String, Object>emptyMap()); for (Field field : fields) { try { responseReader.read(field); fail("expected NullPointerException for field: " + field); } catch (IOException expected) { //expected } } } private void checkReadFields(Map<String, Object> recordSet, Field successField, Field classCastExceptionField, Object expectedSuccessValue) throws IOException { RealResponseReader<Map<String, Object>> responseReader = responseReader(recordSet); assertThat(responseReader.read(successField)).isEqualTo(expectedSuccessValue); try { responseReader.read(classCastExceptionField); fail("expected ClassCastException"); } catch (ClassCastException expected) { // expected } } @SuppressWarnings("unchecked") private static RealResponseReader<Map<String, Object>> responseReader( Map<String, Object> recordSet) { Map<ScalarType, CustomTypeAdapter> customTypeAdapters = new HashMap<>(); customTypeAdapters.put(CUSTOM_TYPE, new CustomTypeAdapter() { @Override public Object decode(String value) { try { return DATE_TIME_FORMAT.parse(value); } catch (ParseException e) { throw new ClassCastException(); } } @Override public String encode(Object value) { return null; } }); return new RealResponseReader<>(EMPTY_OPERATION.variables(), recordSet, new MapFieldValueResolver(), customTypeAdapters, NO_OP_NORMALIZER); } private static final ScalarType CUSTOM_TYPE = new ScalarType() { @Override public String typeName() { return Date.class.getName(); } @Override public Class javaType() { return Date.class; } }; private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyyy-mm-dd"); private static final Operation EMPTY_OPERATION = new Operation() { @Override public String queryDocument() { throw new UnsupportedOperationException(); } @Override public Variables variables() { return EMPTY_VARIABLES; } @Override public ResponseFieldMapper responseFieldMapper() { throw new UnsupportedOperationException(); } @Override public Object wrapData(Data data) { throw new UnsupportedOperationException(); } @Nonnull @Override public OperationName name() { return null; } }; @SuppressWarnings("unchecked") private static final ResponseNormalizer NO_OP_NORMALIZER = new ResponseNormalizer() { @Override public void willResolveRootQuery(Operation operation) { } @Override public void willResolve(Field field, Operation.Variables variables) { } @Override public void didResolve(Field field, Operation.Variables variables) { } @Override public void didParseScalar(Object value) { } @Override public void willParseObject(Field field, Optional objectSource) { } @Override public void didParseObject(Field Field, Optional objectSource) { } @Nonnull @Override public CacheKey resolveCacheKey(@Nonnull Field field, @Nonnull Object record) { return CacheKey.NO_KEY; } @Override public void didParseList(List array) { } @Override public void willParseElement(int atIndex) { } @Override public void didParseElement(int atIndex) { } @Override public void didParseNull() { } @Override public Collection<Record> records() { return Collections.emptyList(); } @Override public Set<String> dependentKeys() { return Collections.emptySet(); } }; }