From 898d939f23f47bf03cd0eea376974b8b99db5bed Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 20 Jan 2021 09:46:59 +0100 Subject: [PATCH] Support interface on interface (#2887) * add interfaces on interfaces to the antlr grammar * implement possibleTypes * added a test case --- .../apollo/compiler/parser/antlr/GraphSDL.g4 | 2 +- .../apollo/compiler/parser/SchemaConverter.kt | 13 - .../parser/sdl/GraphSDLSchemaParser.kt | 8 +- .../compiler/parser/sdl/GraphSdlSchema.kt | 30 +- .../interface_on_interface/GetHuman.java | 591 ++++++++++++++++++ .../interface_on_interface/GetHuman.kt | 253 ++++++++ .../TestOperation.graphql | 14 + .../example/interface_on_interface/schema.sdl | 19 + .../type/CustomType.java | 24 + .../interface_on_interface/type/CustomType.kt | 17 + 10 files changed, 943 insertions(+), 28 deletions(-) delete mode 100644 apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/SchemaConverter.kt create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.java create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.kt create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/TestOperation.graphql create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/schema.sdl create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.java create mode 100644 apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.kt diff --git a/apollo-compiler/src/main/antlr/com/apollographql/apollo/compiler/parser/antlr/GraphSDL.g4 b/apollo-compiler/src/main/antlr/com/apollographql/apollo/compiler/parser/antlr/GraphSDL.g4 index b6c60270704..152d525fc54 100644 --- a/apollo-compiler/src/main/antlr/com/apollographql/apollo/compiler/parser/antlr/GraphSDL.g4 +++ b/apollo-compiler/src/main/antlr/com/apollographql/apollo/compiler/parser/antlr/GraphSDL.g4 @@ -62,7 +62,7 @@ implementsInterface ; interfaceTypeDefinition - : description? INTERFACE name directives? fieldsDefinition + : description? INTERFACE name implementsInterfaces? directives? fieldsDefinition ; fieldsDefinition diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/SchemaConverter.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/SchemaConverter.kt deleted file mode 100644 index edb86d91971..00000000000 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/SchemaConverter.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.apollographql.apollo.compiler.parser - -import com.apollographql.apollo.compiler.parser.introspection.IntrospectionSchema -import com.apollographql.apollo.compiler.parser.introspection.IntrospectionSchema.Companion.wrap -import com.apollographql.apollo.compiler.parser.introspection.toSDL -import com.apollographql.apollo.compiler.parser.sdl.GraphSdlSchema -import com.apollographql.apollo.compiler.parser.sdl.toIntrospectionSchema -import com.apollographql.apollo.compiler.toJson -import java.io.File - -object SchemaConverter { - -} \ No newline at end of file diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSDLSchemaParser.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSDLSchemaParser.kt index a7ec027b33a..3ff6c869a4a 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSDLSchemaParser.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSDLSchemaParser.kt @@ -5,7 +5,6 @@ import com.apollographql.apollo.compiler.parser.antlr.GraphSDLLexer import com.apollographql.apollo.compiler.parser.antlr.GraphSDLParser import com.apollographql.apollo.compiler.parser.error.DocumentParseException import com.apollographql.apollo.compiler.parser.error.ParseException -import com.apollographql.apollo.compiler.parser.sdl.GraphSDLSchemaParser.parse import org.antlr.v4.runtime.ANTLRInputStream import org.antlr.v4.runtime.BaseErrorListener import org.antlr.v4.runtime.CommonTokenStream @@ -100,7 +99,8 @@ object GraphSDLSchemaParser { schema = GraphSdlSchema.Schema( description = schemaDefinition?.description()?.parse(), directives = schemaDefinition?.directives().parse(), - queryRootOperationType = rootOperationType(operationRootTypes, "query", typeDefinitions) ?: throw IllegalStateException("No query root operation type found"), + queryRootOperationType = rootOperationType(operationRootTypes, "query", typeDefinitions) + ?: throw IllegalStateException("No query root operation type found"), mutationRootOperationType = rootOperationType(operationRootTypes, "mutation", typeDefinitions), subscriptionRootOperationType = rootOperationType(operationRootTypes, "subscription", typeDefinitions)), typeDefinitions = typeDefinitions ?: emptyMap() @@ -126,6 +126,7 @@ object GraphSDLSchemaParser { null } } + private fun List.parse( typeDefinitions: Map ): Map { @@ -386,7 +387,8 @@ object GraphSDLSchemaParser { name = name().text, description = description().parse(), directives = directives().parse(), - fields = fieldsDefinition().parse() + fields = fieldsDefinition().parse(), + interfaces = implementsInterfaces().parse() ) } diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSdlSchema.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSdlSchema.kt index 4f31ed5195b..32024281b8d 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSdlSchema.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/sdl/GraphSdlSchema.kt @@ -5,6 +5,7 @@ import com.apollographql.apollo.compiler.parser.error.ParseException import com.apollographql.apollo.compiler.parser.introspection.IntrospectionSchema import com.apollographql.apollo.compiler.parser.sdl.GraphSDLSchemaParser.parse import java.io.File +import java.lang.IllegalStateException data class GraphSdlSchema( val schema: Schema, @@ -51,7 +52,8 @@ data class GraphSdlSchema( override val name: String, override val description: String, override val directives: List, - val fields: List + val fields: List, + val interfaces: List ) : TypeDefinition() data class Field( @@ -298,17 +300,23 @@ private fun List.findDeprecatedDirective(): DeprecateD } } -private fun GraphSdlSchema.TypeDefinition.Interface.possibleTypes(schema: GraphSdlSchema): List { - return schema.typeDefinitions.values - .filter { typeDefinition -> - typeDefinition is GraphSdlSchema.TypeDefinition.Object && typeDefinition.interfaces.firstOrNull { interfaceTypeRef -> - interfaceTypeRef.typeName == name - } != null - } - .map { typeDefinition -> +internal fun GraphSdlSchema.TypeDefinition.possibleTypes(schema: GraphSdlSchema): List { + return when (this) { + is GraphSdlSchema.TypeDefinition.Union -> this.typeRefs.map { it.toIntrospectionType(schema) } + is GraphSdlSchema.TypeDefinition.Interface -> schema.typeDefinitions.values.filter { typeDefinition -> + typeDefinition is GraphSdlSchema.TypeDefinition.Object && typeDefinition.interfaces.map { it.typeName }.contains(name) + || typeDefinition is GraphSdlSchema.TypeDefinition.Interface && typeDefinition.interfaces.map { it.typeName }.contains(name) + }.flatMap { + it.possibleTypes(schema) + } + is GraphSdlSchema.TypeDefinition.Object -> listOf( IntrospectionSchema.TypeRef( kind = IntrospectionSchema.Kind.OBJECT, - name = typeDefinition.name + name = name ) - } + ) + else -> { + throw IllegalStateException("Cannot determine possibleTypes of $name") + } + } } diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.java b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.java new file mode 100644 index 00000000000..13e49d270d7 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.java @@ -0,0 +1,591 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// +// This class was automatically generated by Apollo GraphQL plugin from the GraphQL queries it found. +// It should not be modified by hand. +// +package com.example.interface_on_interface; + +import com.apollographql.apollo.api.Operation; +import com.apollographql.apollo.api.OperationName; +import com.apollographql.apollo.api.Query; +import com.apollographql.apollo.api.Response; +import com.apollographql.apollo.api.ResponseField; +import com.apollographql.apollo.api.ScalarTypeAdapters; +import com.apollographql.apollo.api.internal.OperationRequestBodyComposer; +import com.apollographql.apollo.api.internal.Optional; +import com.apollographql.apollo.api.internal.QueryDocumentMinifier; +import com.apollographql.apollo.api.internal.ResponseFieldMapper; +import com.apollographql.apollo.api.internal.ResponseFieldMarshaller; +import com.apollographql.apollo.api.internal.ResponseReader; +import com.apollographql.apollo.api.internal.ResponseWriter; +import com.apollographql.apollo.api.internal.SimpleOperationResponseParser; +import com.apollographql.apollo.api.internal.Utils; +import com.example.interface_on_interface.type.CustomType; +import java.io.IOException; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.SuppressWarnings; +import java.util.Arrays; +import java.util.Collections; +import okio.Buffer; +import okio.BufferedSource; +import okio.ByteString; +import org.jetbrains.annotations.NotNull; + +public final class GetHuman implements Query, Operation.Variables> { + public static final String OPERATION_ID = "4e95423e0befb11151a4bd58f752ef8761a64ffd32e2d286f35777003023ef70"; + + public static final String QUERY_DOCUMENT = QueryDocumentMinifier.minify( + "query GetHuman {\n" + + " human {\n" + + " __typename\n" + + " id\n" + + " name\n" + + " height\n" + + " }\n" + + " node {\n" + + " __typename\n" + + " ... on Human {\n" + + " height\n" + + " }\n" + + " }\n" + + "}" + ); + + public static final OperationName OPERATION_NAME = new OperationName() { + @Override + public String name() { + return "GetHuman"; + } + }; + + private final Operation.Variables variables; + + public GetHuman() { + this.variables = Operation.EMPTY_VARIABLES; + } + + @Override + public String operationId() { + return OPERATION_ID; + } + + @Override + public String queryDocument() { + return QUERY_DOCUMENT; + } + + @Override + public Optional wrapData(GetHuman.Data data) { + return Optional.fromNullable(data); + } + + @Override + public Operation.Variables variables() { + return variables; + } + + @Override + public ResponseFieldMapper responseFieldMapper() { + return new Data.Mapper(); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public OperationName name() { + return OPERATION_NAME; + } + + @Override + @NotNull + public Response> parse(@NotNull final BufferedSource source, + @NotNull final ScalarTypeAdapters scalarTypeAdapters) throws IOException { + return SimpleOperationResponseParser.parse(source, this, scalarTypeAdapters); + } + + @Override + @NotNull + public Response> parse(@NotNull final ByteString byteString, + @NotNull final ScalarTypeAdapters scalarTypeAdapters) throws IOException { + return parse(new Buffer().write(byteString), scalarTypeAdapters); + } + + @Override + @NotNull + public Response> parse(@NotNull final BufferedSource source) throws + IOException { + return parse(source, ScalarTypeAdapters.DEFAULT); + } + + @Override + @NotNull + public Response> parse(@NotNull final ByteString byteString) throws + IOException { + return parse(byteString, ScalarTypeAdapters.DEFAULT); + } + + @Override + @NotNull + public ByteString composeRequestBody(@NotNull final ScalarTypeAdapters scalarTypeAdapters) { + return OperationRequestBodyComposer.compose(this, false, true, scalarTypeAdapters); + } + + @NotNull + @Override + public ByteString composeRequestBody() { + return OperationRequestBodyComposer.compose(this, false, true, ScalarTypeAdapters.DEFAULT); + } + + @Override + @NotNull + public ByteString composeRequestBody(final boolean autoPersistQueries, + final boolean withQueryDocument, @NotNull final ScalarTypeAdapters scalarTypeAdapters) { + return OperationRequestBodyComposer.compose(this, autoPersistQueries, withQueryDocument, scalarTypeAdapters); + } + + public static final class Builder { + Builder() { + } + + public GetHuman build() { + return new GetHuman(); + } + } + + /** + * Data from the response after executing this GraphQL operation + */ + public static class Data implements Operation.Data { + static final ResponseField[] $responseFields = { + ResponseField.forObject("human", "human", null, false, Collections.emptyList()), + ResponseField.forObject("node", "node", null, false, Collections.emptyList()) + }; + + final @NotNull Human human; + + final @NotNull Node node; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public Data(@NotNull Human human, @NotNull Node node) { + this.human = Utils.checkNotNull(human, "human == null"); + this.node = Utils.checkNotNull(node, "node == null"); + } + + public @NotNull Human human() { + return this.human; + } + + public @NotNull Node node() { + return this.node; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeObject($responseFields[0], human.marshaller()); + writer.writeObject($responseFields[1], node.marshaller()); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "Data{" + + "human=" + human + ", " + + "node=" + node + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Data) { + Data that = (Data) o; + return this.human.equals(that.human) + && this.node.equals(that.node); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= human.hashCode(); + h *= 1000003; + h ^= node.hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + final Human.Mapper humanFieldMapper = new Human.Mapper(); + + final Node.Mapper nodeFieldMapper = new Node.Mapper(); + + @Override + public Data map(ResponseReader reader) { + final Human human = reader.readObject($responseFields[0], new ResponseReader.ObjectReader() { + @Override + public Human read(ResponseReader reader) { + return humanFieldMapper.map(reader); + } + }); + final Node node = reader.readObject($responseFields[1], new ResponseReader.ObjectReader() { + @Override + public Node read(ResponseReader reader) { + return nodeFieldMapper.map(reader); + } + }); + return new Data(human, node); + } + } + } + + public static class Human { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), + ResponseField.forCustomType("id", "id", null, false, CustomType.ID, Collections.emptyList()), + ResponseField.forString("name", "name", null, false, Collections.emptyList()), + ResponseField.forDouble("height", "height", null, false, Collections.emptyList()) + }; + + final @NotNull String __typename; + + final @NotNull String id; + + final @NotNull String name; + + final double height; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public Human(@NotNull String __typename, @NotNull String id, @NotNull String name, + double height) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + this.id = Utils.checkNotNull(id, "id == null"); + this.name = Utils.checkNotNull(name, "name == null"); + this.height = height; + } + + public @NotNull String __typename() { + return this.__typename; + } + + public @NotNull String id() { + return this.id; + } + + public @NotNull String name() { + return this.name; + } + + public double height() { + return this.height; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeString($responseFields[0], __typename); + writer.writeCustom((ResponseField.CustomTypeField) $responseFields[1], id); + writer.writeString($responseFields[2], name); + writer.writeDouble($responseFields[3], height); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "Human{" + + "__typename=" + __typename + ", " + + "id=" + id + ", " + + "name=" + name + ", " + + "height=" + height + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Human) { + Human that = (Human) o; + return this.__typename.equals(that.__typename) + && this.id.equals(that.id) + && this.name.equals(that.name) + && Double.doubleToLongBits(this.height) == Double.doubleToLongBits(that.height); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + h *= 1000003; + h ^= id.hashCode(); + h *= 1000003; + h ^= name.hashCode(); + h *= 1000003; + h ^= Double.valueOf(height).hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + @Override + public Human map(ResponseReader reader) { + final String __typename = reader.readString($responseFields[0]); + final String id = reader.readCustomType((ResponseField.CustomTypeField) $responseFields[1]); + final String name = reader.readString($responseFields[2]); + final double height = reader.readDouble($responseFields[3]); + return new Human(__typename, id, name, height); + } + } + } + + public interface Node { + @NotNull String __typename(); + + ResponseFieldMarshaller marshaller(); + + default T visit(Visitor visitor) { + if (this instanceof AsHuman) { + return visitor.visit((AsHuman) this); + } else if (this instanceof AsNode) { + return visitor.visit((AsNode) this); + } + return visitor.visitDefault(this); + } + + final class Mapper implements ResponseFieldMapper { + static final ResponseField[] $responseFields = { + ResponseField.forFragment("__typename", "__typename", Arrays.asList( + ResponseField.Condition.typeCondition(new String[] {"Human"}) + )) + }; + + final AsHuman.Mapper asHumanFieldMapper = new AsHuman.Mapper(); + + final AsNode.Mapper asNodeFieldMapper = new AsNode.Mapper(); + + @Override + public Node map(ResponseReader reader) { + final AsHuman asHuman = reader.readFragment($responseFields[0], new ResponseReader.ObjectReader() { + @Override + public AsHuman read(ResponseReader reader) { + return asHumanFieldMapper.map(reader); + } + }); + if (asHuman != null) { + return asHuman; + } + return asNodeFieldMapper.map(reader); + } + } + + interface Visitor { + T visitDefault(@NotNull Node node); + + T visit(@NotNull AsHuman asHuman); + + T visit(@NotNull AsNode asNode); + } + } + + public static class AsHuman implements Node { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), + ResponseField.forDouble("height", "height", null, false, Collections.emptyList()) + }; + + final @NotNull String __typename; + + final double height; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public AsHuman(@NotNull String __typename, double height) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + this.height = height; + } + + public @NotNull String __typename() { + return this.__typename; + } + + public double height() { + return this.height; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeString($responseFields[0], __typename); + writer.writeDouble($responseFields[1], height); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "AsHuman{" + + "__typename=" + __typename + ", " + + "height=" + height + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof AsHuman) { + AsHuman that = (AsHuman) o; + return this.__typename.equals(that.__typename) + && Double.doubleToLongBits(this.height) == Double.doubleToLongBits(that.height); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + h *= 1000003; + h ^= Double.valueOf(height).hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + @Override + public AsHuman map(ResponseReader reader) { + final String __typename = reader.readString($responseFields[0]); + final double height = reader.readDouble($responseFields[1]); + return new AsHuman(__typename, height); + } + } + } + + public static class AsNode implements Node { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()) + }; + + final @NotNull String __typename; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public AsNode(@NotNull String __typename) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + } + + public @NotNull String __typename() { + return this.__typename; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeString($responseFields[0], __typename); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "AsNode{" + + "__typename=" + __typename + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof AsNode) { + AsNode that = (AsNode) o; + return this.__typename.equals(that.__typename); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + @Override + public AsNode map(ResponseReader reader) { + final String __typename = reader.readString($responseFields[0]); + return new AsNode(__typename); + } + } + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.kt b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.kt new file mode 100644 index 00000000000..23e4b6043f2 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/GetHuman.kt @@ -0,0 +1,253 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// +// This class was automatically generated by Apollo GraphQL plugin from the GraphQL queries it found. +// It should not be modified by hand. +// +package com.example.interface_on_interface + +import com.apollographql.apollo.api.Operation +import com.apollographql.apollo.api.OperationName +import com.apollographql.apollo.api.Query +import com.apollographql.apollo.api.Response +import com.apollographql.apollo.api.ResponseField +import com.apollographql.apollo.api.ScalarTypeAdapters +import com.apollographql.apollo.api.ScalarTypeAdapters.Companion.DEFAULT +import com.apollographql.apollo.api.internal.OperationRequestBodyComposer +import com.apollographql.apollo.api.internal.QueryDocumentMinifier +import com.apollographql.apollo.api.internal.ResponseFieldMapper +import com.apollographql.apollo.api.internal.ResponseFieldMarshaller +import com.apollographql.apollo.api.internal.ResponseReader +import com.apollographql.apollo.api.internal.SimpleOperationResponseParser +import com.apollographql.apollo.api.internal.Throws +import com.example.interface_on_interface.type.CustomType +import kotlin.Array +import kotlin.Boolean +import kotlin.Double +import kotlin.String +import kotlin.Suppress +import okio.Buffer +import okio.BufferedSource +import okio.ByteString +import okio.IOException + +@Suppress("NAME_SHADOWING", "UNUSED_ANONYMOUS_PARAMETER", "LocalVariableName", + "RemoveExplicitTypeArguments", "NestedLambdaShadowedImplicitParameter") +class GetHuman : Query { + override fun operationId(): String = OPERATION_ID + override fun queryDocument(): String = QUERY_DOCUMENT + override fun wrapData(data: Data?): Data? = data + override fun variables(): Operation.Variables = Operation.EMPTY_VARIABLES + override fun name(): OperationName = OPERATION_NAME + override fun responseFieldMapper(): ResponseFieldMapper = ResponseFieldMapper.invoke { + Data(it) + } + + @Throws(IOException::class) + override fun parse(source: BufferedSource, scalarTypeAdapters: ScalarTypeAdapters): Response + = SimpleOperationResponseParser.parse(source, this, scalarTypeAdapters) + + @Throws(IOException::class) + override fun parse(byteString: ByteString, scalarTypeAdapters: ScalarTypeAdapters): Response + = parse(Buffer().write(byteString), scalarTypeAdapters) + + @Throws(IOException::class) + override fun parse(source: BufferedSource): Response = parse(source, DEFAULT) + + @Throws(IOException::class) + override fun parse(byteString: ByteString): Response = parse(byteString, DEFAULT) + + override fun composeRequestBody(scalarTypeAdapters: ScalarTypeAdapters): ByteString = + OperationRequestBodyComposer.compose( + operation = this, + autoPersistQueries = false, + withQueryDocument = true, + scalarTypeAdapters = scalarTypeAdapters + ) + + override fun composeRequestBody(): ByteString = OperationRequestBodyComposer.compose( + operation = this, + autoPersistQueries = false, + withQueryDocument = true, + scalarTypeAdapters = DEFAULT + ) + + override fun composeRequestBody( + autoPersistQueries: Boolean, + withQueryDocument: Boolean, + scalarTypeAdapters: ScalarTypeAdapters + ): ByteString = OperationRequestBodyComposer.compose( + operation = this, + autoPersistQueries = autoPersistQueries, + withQueryDocument = withQueryDocument, + scalarTypeAdapters = scalarTypeAdapters + ) + + data class Human( + val __typename: String = "Human", + val id: String, + val name: String, + val height: Double + ) { + fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@Human.__typename) + writer.writeCustom(RESPONSE_FIELDS[1] as ResponseField.CustomTypeField, this@Human.id) + writer.writeString(RESPONSE_FIELDS[2], this@Human.name) + writer.writeDouble(RESPONSE_FIELDS[3], this@Human.height) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forCustomType("id", "id", null, false, CustomType.ID, null), + ResponseField.forString("name", "name", null, false, null), + ResponseField.forDouble("height", "height", null, false, null) + ) + + operator fun invoke(reader: ResponseReader): Human = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val id = readCustomType(RESPONSE_FIELDS[1] as ResponseField.CustomTypeField)!! + val name = readString(RESPONSE_FIELDS[2])!! + val height = readDouble(RESPONSE_FIELDS[3])!! + Human( + __typename = __typename, + id = id, + name = name, + height = height + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + interface NodeNode { + fun marshaller(): ResponseFieldMarshaller + } + + data class AsHuman( + val __typename: String = "Human", + val height: Double + ) : NodeNode { + override fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@AsHuman.__typename) + writer.writeDouble(RESPONSE_FIELDS[1], this@AsHuman.height) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forDouble("height", "height", null, false, null) + ) + + operator fun invoke(reader: ResponseReader): AsHuman = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val height = readDouble(RESPONSE_FIELDS[1])!! + AsHuman( + __typename = __typename, + height = height + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + data class Node( + val __typename: String = "Node", + val asHuman: AsHuman? + ) { + fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@Node.__typename) + writer.writeFragment(this@Node.asHuman?.marshaller()) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forFragment("__typename", "__typename", listOf( + ResponseField.Condition.typeCondition(arrayOf("Human")) + )) + ) + + operator fun invoke(reader: ResponseReader): Node = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val asHuman = readFragment(RESPONSE_FIELDS[1]) { reader -> + AsHuman(reader) + } + Node( + __typename = __typename, + asHuman = asHuman + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + /** + * Data from the response after executing this GraphQL operation + */ + data class Data( + val human: Human, + val node: Node + ) : Operation.Data { + override fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeObject(RESPONSE_FIELDS[0], this@Data.human.marshaller()) + writer.writeObject(RESPONSE_FIELDS[1], this@Data.node.marshaller()) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forObject("human", "human", null, false, null), + ResponseField.forObject("node", "node", null, false, null) + ) + + operator fun invoke(reader: ResponseReader): Data = reader.run { + val human = readObject(RESPONSE_FIELDS[0]) { reader -> + Human(reader) + }!! + val node = readObject(RESPONSE_FIELDS[1]) { reader -> + Node(reader) + }!! + Data( + human = human, + node = node + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + companion object { + const val OPERATION_ID: String = + "4e95423e0befb11151a4bd58f752ef8761a64ffd32e2d286f35777003023ef70" + + val QUERY_DOCUMENT: String = QueryDocumentMinifier.minify( + """ + |query GetHuman { + | human { + | __typename + | id + | name + | height + | } + | node { + | __typename + | ... on Human { + | height + | } + | } + |} + """.trimMargin() + ) + + val OPERATION_NAME: OperationName = object : OperationName { + override fun name(): String = "GetHuman" + } + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/TestOperation.graphql b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/TestOperation.graphql new file mode 100644 index 00000000000..e8c873c8203 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/TestOperation.graphql @@ -0,0 +1,14 @@ +query GetHuman { + human { + id + name + height + } + node { + # A parser understanding interfaces on interfaces should not fail here + # as Human is a possibleType of Node + ... on Human { + height + } + } +} \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/schema.sdl b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/schema.sdl new file mode 100644 index 00000000000..7faededb7aa --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/schema.sdl @@ -0,0 +1,19 @@ +type Query { + human: Human! + node: Node! +} + +interface Node { + id: ID! +} + +interface Character implements Node { + id: ID! + name: String! +} + +type Human implements Character { + id: ID! + name: String! + height: Float! +} \ No newline at end of file diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.java b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.java new file mode 100644 index 00000000000..50c41b8cb44 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.java @@ -0,0 +1,24 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// +// This class was automatically generated by Apollo GraphQL plugin from the GraphQL queries it found. +// It should not be modified by hand. +// +package com.example.interface_on_interface.type; + +import com.apollographql.apollo.api.ScalarType; +import java.lang.Override; +import java.lang.String; + +public enum CustomType implements ScalarType { + ID { + @Override + public String typeName() { + return "ID"; + } + + @Override + public String className() { + return "java.lang.String"; + } + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.kt b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.kt new file mode 100644 index 00000000000..191cc2fb062 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/interface_on_interface/type/CustomType.kt @@ -0,0 +1,17 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// +// This class was automatically generated by Apollo GraphQL plugin from the GraphQL queries it found. +// It should not be modified by hand. +// +package com.example.interface_on_interface.type + +import com.apollographql.apollo.api.ScalarType +import kotlin.String + +enum class CustomType : ScalarType { + ID { + override fun typeName(): String = "ID" + + override fun className(): String = "kotlin.String" + } +}