Skip to content

Commit 2760db6

Browse files
authored
Restrict overriding custom scalar types (#140)
1 parent 7c4ab87 commit 2760db6

File tree

20 files changed

+101
-104
lines changed

20 files changed

+101
-104
lines changed

apollo-api/src/main/java/com/apollographql/android/api/graphql/Field.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public final class Field {
1313
private final ObjectReader objectReader;
1414
private final ListReader listReader;
1515
private final boolean optional;
16-
private final TypeMapping typeMapping;
16+
private final ScalarType scalarType;
1717

1818
public static Field forString(String responseName, String fieldName, Map<String, Object> arguments,
1919
boolean optional) {
@@ -55,12 +55,12 @@ public static <T> Field forList(String responseName, String fieldName, Map<Strin
5555
}
5656

5757
public static <T> Field forCustomType(String responseName, String fieldName, Map<String, Object> arguments,
58-
boolean optional, TypeMapping typeMapping) {
59-
return new Field(Type.CUSTOM, responseName, fieldName, arguments, null, null, optional, typeMapping);
58+
boolean optional, ScalarType scalarType) {
59+
return new Field(Type.CUSTOM, responseName, fieldName, arguments, null, null, optional, scalarType);
6060
}
6161

6262
private Field(Type type, String responseName, String fieldName, Map<String, Object> arguments,
63-
ObjectReader objectReader, ListReader listReader, boolean optional, TypeMapping typeMapping) {
63+
ObjectReader objectReader, ListReader listReader, boolean optional, ScalarType scalarType) {
6464
this.type = type;
6565
this.responseName = responseName;
6666
this.fieldName = fieldName;
@@ -69,7 +69,7 @@ private Field(Type type, String responseName, String fieldName, Map<String, Obje
6969
this.objectReader = objectReader;
7070
this.listReader = listReader;
7171
this.optional = optional;
72-
this.typeMapping = typeMapping;
72+
this.scalarType = scalarType;
7373
}
7474

7575
public Type type() {
@@ -100,8 +100,8 @@ public ListReader listReader() {
100100
return listReader;
101101
}
102102

103-
public TypeMapping typeMapping() {
104-
return typeMapping;
103+
public ScalarType scalarType() {
104+
return scalarType;
105105
}
106106

107107
public static enum Type {
@@ -135,6 +135,6 @@ public interface ListItemReader {
135135

136136
Boolean readBoolean() throws IOException;
137137

138-
<T> T readCustomType(TypeMapping typeMapping) throws IOException;
138+
<T> T readCustomType(ScalarType scalarType) throws IOException;
139139
}
140140
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.apollographql.android.api.graphql;
2+
3+
public interface ScalarType {
4+
String typeName();
5+
}

apollo-api/src/main/java/com/apollographql/android/api/graphql/ScalarTypeMapping.java

-7
This file was deleted.

apollo-api/src/main/java/com/apollographql/android/api/graphql/TypeMapping.java

-7
This file was deleted.

apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/CustomEnumTypeSpecBuilder.kt

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.apollographql.android.compiler
22

3-
import com.apollographql.android.api.graphql.TypeMapping
3+
import com.apollographql.android.api.graphql.ScalarType
44
import com.apollographql.android.compiler.ir.CodeGenerationContext
55
import com.squareup.javapoet.ClassName
66
import com.squareup.javapoet.MethodSpec
@@ -13,32 +13,26 @@ class CustomEnumTypeSpecBuilder(
1313
fun build(): TypeSpec =
1414
TypeSpec.enumBuilder(className(context))
1515
.addAnnotation(Annotations.GENERATED_BY_APOLLO)
16-
.addSuperinterface(TypeMapping::class.java)
16+
.addSuperinterface(ScalarType::class.java)
1717
.addModifiers(Modifier.PUBLIC)
1818
.addEnumConstants()
1919
.build()
2020

2121
private fun TypeSpec.Builder.addEnumConstants(): TypeSpec.Builder {
2222
context.customTypeMap.forEach { mapping ->
2323
val constantName = mapping.key.removeSuffix("!").toUpperCase()
24-
addEnumConstant(constantName, scalarMappingTypeSpec(mapping.key, mapping.value))
24+
addEnumConstant(constantName, scalarMappingTypeSpec(mapping.key))
2525
}
2626
return this
2727
}
2828

29-
private fun scalarMappingTypeSpec(scalarType: String, javaClassName: String) =
29+
private fun scalarMappingTypeSpec(scalarType: String) =
3030
TypeSpec.anonymousClassBuilder("")
31-
.addMethod(MethodSpec.methodBuilder("type")
31+
.addMethod(MethodSpec.methodBuilder("typeName")
3232
.addModifiers(Modifier.PUBLIC)
3333
.returns(java.lang.String::class.java)
3434
.addStatement("return \$S", scalarType)
3535
.build())
36-
.addMethod(MethodSpec.methodBuilder("clazz")
37-
.addModifiers(Modifier.PUBLIC)
38-
.returns(Class::class.java)
39-
.addStatement("return \$T.class", ClassName.get(javaClassName.substringBeforeLast("."),
40-
javaClassName.substringAfterLast(".")))
41-
.build())
4236
.build()
4337

4438
companion object {

apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/GraphQLCompiler.kt

+17-8
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,22 @@ open class GraphQLCompiler {
1515
val irPackageName = irFile.absolutePath.formatPackageName()
1616
val fragmentsPackage = if (irPackageName.length > 0) "$irPackageName.fragment" else "fragment"
1717
val typesPackage = if (irPackageName.length > 0) "$irPackageName.type" else "type"
18+
val supportedScalarTypeMapping = customTypeMap.supportedScalarTypeMapping(ir.typesUsed)
1819
val codeGenerationContext = CodeGenerationContext(!generateClasses, emptyList(), ir.typesUsed, fragmentsPackage,
19-
typesPackage, customTypeMap)
20+
typesPackage, supportedScalarTypeMapping)
2021
val operationTypeBuilders = ir.operations.map { OperationTypeSpecBuilder(it, ir.fragments) }
21-
(operationTypeBuilders + ir.fragments + ir.typesUsed).forEach {
22+
(operationTypeBuilders + ir.fragments + ir.typesUsed.supportedTypeDeclarations()).forEach {
2223
val packageName = javaFilePackageName(it, irPackageName, fragmentsPackage, typesPackage)
2324
val typeSpec = it.toTypeSpec(codeGenerationContext)
2425
JavaFile.builder(packageName, typeSpec).build().writeTo(outputDir)
2526
}
2627

27-
if (customTypeMap.isNotEmpty()) {
28+
if (supportedScalarTypeMapping.isNotEmpty()) {
2829
val typeSpec = CustomEnumTypeSpecBuilder(codeGenerationContext).build()
2930
JavaFile.builder(typesPackage, typeSpec).build().writeTo(outputDir)
3031
}
3132
}
3233

33-
companion object {
34-
const val FILE_EXTENSION = "graphql"
35-
val OUTPUT_DIRECTORY = listOf("generated", "source", "apollo")
36-
}
37-
3834
private fun String.formatPackageName(): String {
3935
val parts = split(File.separatorChar)
4036
(parts.size - 1 downTo 2)
@@ -52,4 +48,17 @@ open class GraphQLCompiler {
5248
}
5349
return irPackage
5450
}
51+
52+
private fun List<TypeDeclaration>.supportedTypeDeclarations() =
53+
filter { it.kind == TypeDeclaration.KIND_ENUM || it.kind == TypeDeclaration.KIND_INPUT_OBJECT_TYPE }
54+
55+
private fun Map<String, String>.supportedScalarTypeMapping(typeDeclarations: List<TypeDeclaration>): Map<String, String> {
56+
val customScalarTypes = typeDeclarations.filter { it.kind == TypeDeclaration.KIND_SCALAR_TYPE }.map { it.name }
57+
return filter { customScalarTypes.contains(it.key) }
58+
}
59+
60+
companion object {
61+
const val FILE_EXTENSION = "graphql"
62+
val OUTPUT_DIRECTORY = listOf("generated", "source", "apollo")
63+
}
5564
}

apollo-compiler/src/main/kotlin/com/apollographql/android/compiler/ir/TypeDeclaration.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ data class TypeDeclaration(
1313
val fields: List<TypeDeclarationField>?
1414
) : CodeGenerator {
1515
override fun toTypeSpec(context: CodeGenerationContext): TypeSpec {
16-
if (kind == "EnumType") {
16+
if (kind == KIND_ENUM) {
1717
return enumTypeToTypeSpec()
18-
} else if (kind == "InputObjectType") {
18+
} else if (kind == KIND_INPUT_OBJECT_TYPE) {
1919
return inputObjectToTypeSpec(context)
2020
} else {
2121
throw UnsupportedOperationException("unsupported $kind type declaration")
@@ -42,4 +42,10 @@ data class TypeDeclaration(
4242

4343
private fun inputObjectToTypeSpec(context: CodeGenerationContext) =
4444
InputObjectTypeSpecBuilder(name, fields ?: emptyList(), context).build()
45+
46+
companion object {
47+
val KIND_INPUT_OBJECT_TYPE : String = "InputObjectType"
48+
val KIND_ENUM : String = "EnumType"
49+
val KIND_SCALAR_TYPE : String = "ScalarType"
50+
}
4551
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
package com.example.custom_scalar_type.type;
22

3-
import com.apollographql.android.api.graphql.TypeMapping;
4-
import java.lang.Class;
3+
import com.apollographql.android.api.graphql.ScalarType;
54
import java.lang.String;
6-
import java.util.Date;
75
import javax.annotation.Generated;
86

97
@Generated("Apollo GraphQL")
10-
public enum CustomType implements TypeMapping {
8+
public enum CustomType implements ScalarType {
119
DATE {
12-
public String type() {
10+
public String typeName() {
1311
return "Date";
1412
}
15-
16-
public Class clazz() {
17-
return Date.class;
18-
}
1913
}
2014
}

apollo-compiler/src/test/graphql/com/example/custom_scalar_type/TestQuery.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,11 @@
3636
}
3737
],
3838
"fragments": [],
39-
"typesUsed": []
39+
"typesUsed": [
40+
{
41+
"kind": "ScalarType",
42+
"name": "Date",
43+
"description": null
44+
}
45+
]
4046
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
package com.example.pojo_custom_scalar_type.type;
22

3-
import com.apollographql.android.api.graphql.TypeMapping;
4-
import java.lang.Class;
3+
import com.apollographql.android.api.graphql.ScalarType;
54
import java.lang.String;
6-
import java.util.Date;
75
import javax.annotation.Generated;
86

97
@Generated("Apollo GraphQL")
10-
public enum CustomType implements TypeMapping {
8+
public enum CustomType implements ScalarType {
119
DATE {
12-
public String type() {
10+
public String typeName() {
1311
return "Date";
1412
}
15-
16-
public Class clazz() {
17-
return Date.class;
18-
}
1913
}
2014
}

apollo-compiler/src/test/graphql/com/example/pojo_custom_scalar_type/TestQuery.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,11 @@
3636
}
3737
],
3838
"fragments": [],
39-
"typesUsed": []
39+
"typesUsed": [
40+
{
41+
"kind": "ScalarType",
42+
"name": "Date",
43+
"description": null
44+
}
45+
]
4046
}

apollo-compiler/src/test/kotlin/com/apollographql/android/compiler/CodegenTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ class CodeGenTest(val testDir: File, val pkgName: String, val generatePOJO: Bool
7171
.filter { it.isDirectory }
7272
.map {
7373
if (it.name == "custom_scalar_type") {
74-
arrayOf(it, it.name, false, mapOf("Date" to "java.util.Date"))
74+
arrayOf(it, it.name, false, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object"))
7575
} else if (it.name == "pojo_custom_scalar_type") {
76-
arrayOf(it, it.name, true, mapOf("Date" to "java.util.Date"))
76+
arrayOf(it, it.name, true, mapOf("Date" to "java.util.Date", "UnsupportedType" to "java.lang.Object"))
7777
} else {
7878
arrayOf(it, it.name, it.name.startsWith("pojo"), emptyMap<String, String>())
7979
}

apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloConverterFactory.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import com.apollographql.android.api.graphql.Operation;
44
import com.apollographql.android.api.graphql.Response;
5-
import com.apollographql.android.api.graphql.TypeMapping;
5+
import com.apollographql.android.api.graphql.ScalarType;
66

77
import java.lang.annotation.Annotation;
88
import java.lang.reflect.ParameterizedType;
@@ -18,9 +18,9 @@
1818

1919
/** TODO */
2020
public final class ApolloConverterFactory extends Converter.Factory {
21-
private final Map<TypeMapping, CustomTypeAdapter> customTypeAdapters;
21+
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
2222

23-
private ApolloConverterFactory(Map<TypeMapping, CustomTypeAdapter> customTypeAdapters) {
23+
private ApolloConverterFactory(Map<ScalarType, CustomTypeAdapter> customTypeAdapters) {
2424
this.customTypeAdapters = customTypeAdapters;
2525
}
2626

@@ -36,11 +36,11 @@ private ApolloConverterFactory(Map<TypeMapping, CustomTypeAdapter> customTypeAda
3636
}
3737

3838
public static class Builder {
39-
private final Map<TypeMapping, CustomTypeAdapter> customTypeAdapters = new HashMap<>();
39+
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters = new HashMap<>();
4040

41-
public Builder withCustomTypeAdapter(@Nonnull TypeMapping typeMapping,
41+
public Builder withCustomTypeAdapter(@Nonnull ScalarType scalarType,
4242
@Nonnull CustomTypeAdapter customTypeAdapter) {
43-
customTypeAdapters.put(typeMapping, customTypeAdapter);
43+
customTypeAdapters.put(scalarType, customTypeAdapter);
4444
return this;
4545
}
4646

apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/ApolloResponseBodyConverter.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.apollographql.android.api.graphql.Operation;
66
import com.apollographql.android.api.graphql.Response;
77
import com.apollographql.android.api.graphql.ResponseReader;
8-
import com.apollographql.android.api.graphql.TypeMapping;
8+
import com.apollographql.android.api.graphql.ScalarType;
99

1010
import java.io.IOException;
1111
import java.lang.reflect.InvocationTargetException;
@@ -18,9 +18,9 @@
1818

1919
class ApolloResponseBodyConverter implements Converter<ResponseBody, Response<? extends Operation.Data>> {
2020
private final Type type;
21-
private final Map<TypeMapping, CustomTypeAdapter> customTypeAdapters;
21+
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
2222

23-
ApolloResponseBodyConverter(Type type, Map<TypeMapping, CustomTypeAdapter> customTypeAdapters) {
23+
ApolloResponseBodyConverter(Type type, Map<ScalarType, CustomTypeAdapter> customTypeAdapters) {
2424
this.type = type;
2525
this.customTypeAdapters = customTypeAdapters;
2626
}

apollo-converters/pojo/src/main/java/com/apollographql/android/converter/pojo/BufferedResponseReader.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import com.apollographql.android.api.graphql.Field;
44
import com.apollographql.android.api.graphql.ResponseReader;
5-
import com.apollographql.android.api.graphql.TypeMapping;
5+
import com.apollographql.android.api.graphql.ScalarType;
66

77
import java.io.IOException;
88
import java.math.BigDecimal;
@@ -12,9 +12,9 @@
1212

1313
@SuppressWarnings("WeakerAccess") final class BufferedResponseReader implements ResponseReader {
1414
private final Map<String, Object> buffer;
15-
private final Map<TypeMapping, CustomTypeAdapter> customTypeAdapters;
15+
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
1616

17-
BufferedResponseReader(Map<String, Object> buffer, Map<TypeMapping, CustomTypeAdapter> customTypeAdapters) {
17+
BufferedResponseReader(Map<String, Object> buffer, Map<ScalarType, CustomTypeAdapter> customTypeAdapters) {
1818
this.buffer = buffer;
1919
this.customTypeAdapters = customTypeAdapters;
2020
}
@@ -145,9 +145,9 @@ Boolean readBoolean(Field field) throws IOException {
145145
if (value == null) {
146146
return null;
147147
} else {
148-
CustomTypeAdapter<T> typeAdapter = customTypeAdapters.get(field.typeMapping());
148+
CustomTypeAdapter<T> typeAdapter = customTypeAdapters.get(field.scalarType());
149149
if (typeAdapter == null) {
150-
throw new RuntimeException("Can't resolve custom type adapter for " + field.typeMapping().type());
150+
throw new RuntimeException("Can't resolve custom type adapter for " + field.scalarType().typeName());
151151
}
152152
return typeAdapter.decode(value.toString());
153153
}
@@ -162,9 +162,9 @@ private void checkValue(Object value, boolean optional) {
162162

163163
private static class BufferedListItemReader implements Field.ListItemReader {
164164
private final Object value;
165-
private final Map<TypeMapping, CustomTypeAdapter> customTypeAdapters;
165+
private final Map<ScalarType, CustomTypeAdapter> customTypeAdapters;
166166

167-
BufferedListItemReader(Object value, Map<TypeMapping, CustomTypeAdapter> customTypeAdapters) {
167+
BufferedListItemReader(Object value, Map<ScalarType, CustomTypeAdapter> customTypeAdapters) {
168168
this.value = value;
169169
this.customTypeAdapters = customTypeAdapters;
170170
}
@@ -189,10 +189,10 @@ private static class BufferedListItemReader implements Field.ListItemReader {
189189
return (Boolean) value;
190190
}
191191

192-
@SuppressWarnings("unchecked") @Override public <T> T readCustomType(TypeMapping typeMapping) throws IOException {
193-
CustomTypeAdapter<T> typeAdapter = customTypeAdapters.get(typeMapping);
192+
@SuppressWarnings("unchecked") @Override public <T> T readCustomType(ScalarType scalarType) throws IOException {
193+
CustomTypeAdapter<T> typeAdapter = customTypeAdapters.get(scalarType);
194194
if (typeAdapter == null) {
195-
throw new RuntimeException("Can't resolve custom type adapter for " + typeMapping.type());
195+
throw new RuntimeException("Can't resolve custom type adapter for " + scalarType.typeName());
196196
}
197197
return typeAdapter.decode(value.toString());
198198
}

0 commit comments

Comments
 (0)