From d9039bf023b48c0758ec448d47c0854dc8dabfbe Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 28 Oct 2020 12:21:56 +0100 Subject: [PATCH] Add support for default variable values. (#2704) This only works for scalar types and their list/optional variants, not input object types closes #2703 and part of #2686 --- .../ast/builder/OperationTypeBuilder.kt | 2 +- .../compiler/codegen/kotlin/KotlinCodeGen.kt | 1 + .../compiler/codegen/kotlin/OperationType.kt | 20 +- .../apollo/compiler/ir/Variable.kt | 1 + .../parser/graphql/GraphQLDocumentParser.kt | 3 + .../variable_default_value/GetHero.java | 742 ++++++++++++++++++ .../example/variable_default_value/GetHero.kt | 324 ++++++++ .../TestOperation.graphql | 27 + .../type/CustomType.java | 24 + .../variable_default_value/type/CustomType.kt | 17 + .../type/LengthUnit.java | 47 ++ .../variable_default_value/type/LengthUnit.kt | 35 + 12 files changed, 1239 insertions(+), 4 deletions(-) create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.java create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.kt create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/TestOperation.graphql create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/type/CustomType.java create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/type/CustomType.kt create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.java create mode 100644 apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.kt diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ast/builder/OperationTypeBuilder.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ast/builder/OperationTypeBuilder.kt index df6c9e1d3bb..7baaf52280b 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ast/builder/OperationTypeBuilder.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ast/builder/OperationTypeBuilder.kt @@ -56,7 +56,7 @@ internal fun Operation.ast( typesPackageName = context.typesPackageName ), isOptional = variable.optional(), - defaultValue = null, + defaultValue = variable.defaultValue, description = "" ) } diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/KotlinCodeGen.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/KotlinCodeGen.kt index ec8880f0e80..a3858f8e34d 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/KotlinCodeGen.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/KotlinCodeGen.kt @@ -399,6 +399,7 @@ internal object KotlinCodeGen { } } + // TODO: fix for input object types fun Any.toDefaultValueCodeBlock(typeName: TypeName, fieldType: FieldType): CodeBlock = when { this is Number -> CodeBlock.of("%L%L", castTo(typeName), if (typeName == LONG) "L" else "") fieldType is FieldType.Scalar.Enum -> CodeBlock.of("%T.safeValueOf(%S)", typeName, this) diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/OperationType.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/OperationType.kt index 1f1249b3b28..559166e669c 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/OperationType.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/OperationType.kt @@ -23,6 +23,7 @@ import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.createMapp import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.marshallerFunSpec import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.responseFieldsPropertySpec import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.suppressWarningsAnnotation +import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.toDefaultValueCodeBlock import com.apollographql.apollo.compiler.codegen.kotlin.KotlinCodeGen.toMapperFun import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock @@ -165,13 +166,26 @@ private val OperationType.primaryConstructorSpec: FunSpec return FunSpec .constructorBuilder() .addParameters(variables.fields.map { variable -> - val typeName = variable.type.asTypeName() + val typeName = variable.type.asTypeName().let { + if (variable.isOptional) Input::class.asClassName().parameterizedBy(it) else it + } + val defaultValue = variable.defaultValue?.toDefaultValueCodeBlock(typeName, variable.type) + .let { code -> + if (variable.isOptional) { + code?.let { CodeBlock.of("%T.optional(%L)", Input::class, it) } ?: CodeBlock.of("%T.absent()", Input::class) + } else { + code + } + } + ParameterSpec .builder( name = variable.name, - type = if (variable.isOptional) Input::class.asClassName().parameterizedBy(typeName) else typeName + type = typeName ) - .applyIf(variable.isOptional) { defaultValue("%T.absent()", Input::class.asClassName()) } + .applyIf(defaultValue != null) { + defaultValue(defaultValue!!) + } .build() }) .build() diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ir/Variable.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ir/Variable.kt index ac39751315a..d99a77288ba 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ir/Variable.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/ir/Variable.kt @@ -3,6 +3,7 @@ package com.apollographql.apollo.compiler.ir data class Variable( val name: String, val type: String, + val defaultValue: Any?, val sourceLocation: SourceLocation ) { fun optional(): Boolean = !type.endsWith(suffix = "!") diff --git a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/graphql/GraphQLDocumentParser.kt b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/graphql/GraphQLDocumentParser.kt index 6f742eaa9e5..f9da824e0d2 100644 --- a/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/graphql/GraphQLDocumentParser.kt +++ b/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/parser/graphql/GraphQLDocumentParser.kt @@ -20,6 +20,7 @@ import com.apollographql.apollo.compiler.parser.introspection.IntrospectionSchem import com.apollographql.apollo.compiler.parser.introspection.asGraphQLType import com.apollographql.apollo.compiler.parser.introspection.isAssignableFrom import com.apollographql.apollo.compiler.parser.introspection.possibleTypes +import com.apollographql.apollo.compiler.parser.introspection.resolveType import org.antlr.v4.runtime.ANTLRInputStream import org.antlr.v4.runtime.BaseErrorListener import org.antlr.v4.runtime.CommonTokenStream @@ -232,10 +233,12 @@ class GraphQLDocumentParser( message = "Unknown variable type `$type`", token = type().start ) + return ParseResult( result = Variable( name = name, type = type, + defaultValue = defaultValue()?.value()?.parse(schema.resolveType(type)), sourceLocation = SourceLocation(variable().NAME().symbol) ), usedTypes = setOf(schemaType.name) diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.java b/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.java new file mode 100644 index 00000000000..441227cfc87 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.java @@ -0,0 +1,742 @@ +// 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.variable_default_value; + +import com.apollographql.apollo.api.Input; +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.InputFieldMarshaller; +import com.apollographql.apollo.api.internal.InputFieldWriter; +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.UnmodifiableMapBuilder; +import com.apollographql.apollo.api.internal.Utils; +import com.example.variable_default_value.type.LengthUnit; +import java.io.IOException; +import java.lang.Boolean; +import java.lang.Double; +import java.lang.Integer; +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 java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import okio.Buffer; +import okio.BufferedSource; +import okio.ByteString; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class GetHero implements Query, GetHero.Variables> { + public static final String OPERATION_ID = "174c448da54ce10fe5ade777417fe38dcbc242e68cd54f905a7c6e465e02fdf1"; + + public static final String QUERY_DOCUMENT = QueryDocumentMinifier.minify( + "query GetHero($myBool: Boolean = true, $unit: LengthUnit! = FOOT, $listOfInts: [Int] = [1, 2, 3]) {\n" + + " hero {\n" + + " __typename\n" + + " name @include(if: $myBool)\n" + + " ... on Human {\n" + + " height(unit: $unit)\n" + + " }\n" + + " }\n" + + " heroWithReview(listOfInts: $listOfInts) {\n" + + " __typename\n" + + " name\n" + + " }\n" + + "}" + ); + + public static final OperationName OPERATION_NAME = new OperationName() { + @Override + public String name() { + return "GetHero"; + } + }; + + private final GetHero.Variables variables; + + public GetHero(@NotNull Input myBool, @NotNull LengthUnit unit, + @NotNull Input> listOfInts) { + Utils.checkNotNull(myBool, "myBool == null"); + Utils.checkNotNull(unit, "unit == null"); + Utils.checkNotNull(listOfInts, "listOfInts == null"); + variables = new GetHero.Variables(myBool, unit, listOfInts); + } + + @Override + public String operationId() { + return OPERATION_ID; + } + + @Override + public String queryDocument() { + return QUERY_DOCUMENT; + } + + @Override + public Optional wrapData(GetHero.Data data) { + return Optional.fromNullable(data); + } + + @Override + public GetHero.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 { + private Input myBool = Input.absent(); + + private @NotNull LengthUnit unit; + + private Input> listOfInts = Input.absent(); + + Builder() { + } + + public Builder myBool(@Nullable Boolean myBool) { + this.myBool = Input.fromNullable(myBool); + return this; + } + + public Builder unit(@NotNull LengthUnit unit) { + this.unit = unit; + return this; + } + + public Builder listOfInts(@Nullable List listOfInts) { + this.listOfInts = Input.fromNullable(listOfInts); + return this; + } + + public Builder myBoolInput(@NotNull Input myBool) { + this.myBool = Utils.checkNotNull(myBool, "myBool == null"); + return this; + } + + public Builder listOfIntsInput(@NotNull Input> listOfInts) { + this.listOfInts = Utils.checkNotNull(listOfInts, "listOfInts == null"); + return this; + } + + public GetHero build() { + Utils.checkNotNull(unit, "unit == null"); + return new GetHero(myBool, unit, listOfInts); + } + } + + public static final class Variables extends Operation.Variables { + private final Input myBool; + + private final @NotNull LengthUnit unit; + + private final Input> listOfInts; + + private final transient Map valueMap = new LinkedHashMap<>(); + + Variables(Input myBool, @NotNull LengthUnit unit, Input> listOfInts) { + this.myBool = myBool; + this.unit = unit; + this.listOfInts = listOfInts; + if (myBool.defined) { + this.valueMap.put("myBool", myBool.value); + } + this.valueMap.put("unit", unit); + if (listOfInts.defined) { + this.valueMap.put("listOfInts", listOfInts.value); + } + } + + public Input myBool() { + return myBool; + } + + public @NotNull LengthUnit unit() { + return unit; + } + + public Input> listOfInts() { + return listOfInts; + } + + @Override + public Map valueMap() { + return Collections.unmodifiableMap(valueMap); + } + + @Override + public InputFieldMarshaller marshaller() { + return new InputFieldMarshaller() { + @Override + public void marshal(InputFieldWriter writer) throws IOException { + if (myBool.defined) { + writer.writeBoolean("myBool", myBool.value); + } + writer.writeString("unit", unit.rawValue()); + if (listOfInts.defined) { + writer.writeList("listOfInts", listOfInts.value != null ? new InputFieldWriter.ListWriter() { + @Override + public void write(InputFieldWriter.ListItemWriter listItemWriter) throws IOException { + for (final Integer $item : listOfInts.value) { + listItemWriter.writeInt($item); + } + } + } : null); + } + } + }; + } + } + + /** + * Data from the response after executing this GraphQL operation + */ + public static class Data implements Operation.Data { + static final ResponseField[] $responseFields = { + ResponseField.forObject("hero", "hero", null, true, Collections.emptyList()), + ResponseField.forObject("heroWithReview", "heroWithReview", new UnmodifiableMapBuilder(1) + .put("listOfInts", new UnmodifiableMapBuilder(2) + .put("kind", "Variable") + .put("variableName", "listOfInts") + .build()) + .build(), true, Collections.emptyList()) + }; + + final Optional hero; + + final Optional heroWithReview; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public Data(@Nullable Hero hero, @Nullable HeroWithReview heroWithReview) { + this.hero = Optional.fromNullable(hero); + this.heroWithReview = Optional.fromNullable(heroWithReview); + } + + public Optional hero() { + return this.hero; + } + + public Optional heroWithReview() { + return this.heroWithReview; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeObject($responseFields[0], hero.isPresent() ? hero.get().marshaller() : null); + writer.writeObject($responseFields[1], heroWithReview.isPresent() ? heroWithReview.get().marshaller() : null); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "Data{" + + "hero=" + hero + ", " + + "heroWithReview=" + heroWithReview + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Data) { + Data that = (Data) o; + return this.hero.equals(that.hero) + && this.heroWithReview.equals(that.heroWithReview); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= hero.hashCode(); + h *= 1000003; + h ^= heroWithReview.hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + final Hero.Mapper heroFieldMapper = new Hero.Mapper(); + + final HeroWithReview.Mapper heroWithReviewFieldMapper = new HeroWithReview.Mapper(); + + @Override + public Data map(ResponseReader reader) { + final Hero hero = reader.readObject($responseFields[0], new ResponseReader.ObjectReader() { + @Override + public Hero read(ResponseReader reader) { + return heroFieldMapper.map(reader); + } + }); + final HeroWithReview heroWithReview = reader.readObject($responseFields[1], new ResponseReader.ObjectReader() { + @Override + public HeroWithReview read(ResponseReader reader) { + return heroWithReviewFieldMapper.map(reader); + } + }); + return new Data(hero, heroWithReview); + } + } + } + + /** + * A character from the Star Wars universe + */ + public interface Hero { + @NotNull String __typename(); + + /** + * The name of the character + */ + Optional name(); + + ResponseFieldMarshaller marshaller(); + + default T visit(Visitor visitor) { + if (this instanceof AsHuman) { + return visitor.visit((AsHuman) this); + } else if (this instanceof AsCharacter) { + return visitor.visit((AsCharacter) 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 AsCharacter.Mapper asCharacterFieldMapper = new AsCharacter.Mapper(); + + @Override + public Hero 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 asCharacterFieldMapper.map(reader); + } + } + + interface Visitor { + T visitDefault(@NotNull Hero hero); + + T visit(@NotNull AsHuman asHuman); + + T visit(@NotNull AsCharacter asCharacter); + } + } + + /** + * A humanoid creature from the Star Wars universe + */ + public static class AsHuman implements Hero { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), + ResponseField.forString("name", "name", null, true, Arrays.asList( + ResponseField.Condition.booleanCondition("myBool", false) + )), + ResponseField.forDouble("height", "height", new UnmodifiableMapBuilder(1) + .put("unit", new UnmodifiableMapBuilder(2) + .put("kind", "Variable") + .put("variableName", "unit") + .build()) + .build(), true, Collections.emptyList()) + }; + + final @NotNull String __typename; + + final Optional name; + + final Optional height; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public AsHuman(@NotNull String __typename, @Nullable String name, @Nullable Double height) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + this.name = Optional.fromNullable(name); + this.height = Optional.fromNullable(height); + } + + public @NotNull String __typename() { + return this.__typename; + } + + /** + * What this human calls themselves + */ + public Optional name() { + return this.name; + } + + /** + * Height in the preferred unit, default is meters + */ + public Optional 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.writeString($responseFields[1], name.isPresent() ? name.get() : null); + writer.writeDouble($responseFields[2], height.isPresent() ? height.get() : null); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "AsHuman{" + + "__typename=" + __typename + ", " + + "name=" + name + ", " + + "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) + && this.name.equals(that.name) + && this.height.equals(that.height); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + h *= 1000003; + h ^= name.hashCode(); + h *= 1000003; + h ^= 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 String name = reader.readString($responseFields[1]); + final Double height = reader.readDouble($responseFields[2]); + return new AsHuman(__typename, name, height); + } + } + } + + /** + * A character from the Star Wars universe + */ + public static class AsCharacter implements Hero { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), + ResponseField.forString("name", "name", null, true, Arrays.asList( + ResponseField.Condition.booleanCondition("myBool", false) + )) + }; + + final @NotNull String __typename; + + final Optional name; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public AsCharacter(@NotNull String __typename, @Nullable String name) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + this.name = Optional.fromNullable(name); + } + + public @NotNull String __typename() { + return this.__typename; + } + + /** + * The name of the character + */ + public Optional name() { + return this.name; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeString($responseFields[0], __typename); + writer.writeString($responseFields[1], name.isPresent() ? name.get() : null); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "AsCharacter{" + + "__typename=" + __typename + ", " + + "name=" + name + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof AsCharacter) { + AsCharacter that = (AsCharacter) o; + return this.__typename.equals(that.__typename) + && this.name.equals(that.name); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + h *= 1000003; + h ^= name.hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + @Override + public AsCharacter map(ResponseReader reader) { + final String __typename = reader.readString($responseFields[0]); + final String name = reader.readString($responseFields[1]); + return new AsCharacter(__typename, name); + } + } + } + + /** + * A humanoid creature from the Star Wars universe + */ + public static class HeroWithReview { + static final ResponseField[] $responseFields = { + ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), + ResponseField.forString("name", "name", null, false, Collections.emptyList()) + }; + + final @NotNull String __typename; + + final @NotNull String name; + + private transient volatile String $toString; + + private transient volatile int $hashCode; + + private transient volatile boolean $hashCodeMemoized; + + public HeroWithReview(@NotNull String __typename, @NotNull String name) { + this.__typename = Utils.checkNotNull(__typename, "__typename == null"); + this.name = Utils.checkNotNull(name, "name == null"); + } + + public @NotNull String __typename() { + return this.__typename; + } + + /** + * What this human calls themselves + */ + public @NotNull String name() { + return this.name; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ResponseFieldMarshaller marshaller() { + return new ResponseFieldMarshaller() { + @Override + public void marshal(ResponseWriter writer) { + writer.writeString($responseFields[0], __typename); + writer.writeString($responseFields[1], name); + } + }; + } + + @Override + public String toString() { + if ($toString == null) { + $toString = "HeroWithReview{" + + "__typename=" + __typename + ", " + + "name=" + name + + "}"; + } + return $toString; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof HeroWithReview) { + HeroWithReview that = (HeroWithReview) o; + return this.__typename.equals(that.__typename) + && this.name.equals(that.name); + } + return false; + } + + @Override + public int hashCode() { + if (!$hashCodeMemoized) { + int h = 1; + h *= 1000003; + h ^= __typename.hashCode(); + h *= 1000003; + h ^= name.hashCode(); + $hashCode = h; + $hashCodeMemoized = true; + } + return $hashCode; + } + + public static final class Mapper implements ResponseFieldMapper { + @Override + public HeroWithReview map(ResponseReader reader) { + final String __typename = reader.readString($responseFields[0]); + final String name = reader.readString($responseFields[1]); + return new HeroWithReview(__typename, name); + } + } + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.kt b/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.kt new file mode 100644 index 00000000000..5758d4e10f0 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/GetHero.kt @@ -0,0 +1,324 @@ +// 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.variable_default_value + +import com.apollographql.apollo.api.Input +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.InputFieldMarshaller +import com.apollographql.apollo.api.internal.InputFieldWriter +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.variable_default_value.type.LengthUnit +import kotlin.Any +import kotlin.Array +import kotlin.Boolean +import kotlin.Double +import kotlin.Int +import kotlin.String +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.Map +import kotlin.jvm.Transient +import okio.Buffer +import okio.BufferedSource +import okio.ByteString +import okio.IOException + +@Suppress("NAME_SHADOWING", "UNUSED_ANONYMOUS_PARAMETER", "LocalVariableName", + "RemoveExplicitTypeArguments", "NestedLambdaShadowedImplicitParameter") +data class GetHero( + val myBool: Input = Input.optional(true), + val unit: LengthUnit = LengthUnit.safeValueOf("FOOT"), + val listOfInts: Input> = Input.optional(listOf(1, 2, 3)) +) : Query { + @Transient + private val variables: Operation.Variables = object : Operation.Variables() { + override fun valueMap(): Map = mutableMapOf().apply { + if (this@GetHero.myBool.defined) { + this["myBool"] = this@GetHero.myBool.value + } + this["unit"] = this@GetHero.unit + if (this@GetHero.listOfInts.defined) { + this["listOfInts"] = this@GetHero.listOfInts.value + } + } + + override fun marshaller(): InputFieldMarshaller = InputFieldMarshaller.invoke { writer -> + if (this@GetHero.myBool.defined) { + writer.writeBoolean("myBool", this@GetHero.myBool.value) + } + writer.writeString("unit", this@GetHero.unit.rawValue) + if (this@GetHero.listOfInts.defined) { + writer.writeList("listOfInts", this@GetHero.listOfInts.value?.let { value -> + InputFieldWriter.ListWriter { listItemWriter -> + value.forEach { value -> + listItemWriter.writeInt(value) + } + } + }) + } + } + } + + override fun operationId(): String = OPERATION_ID + override fun queryDocument(): String = QUERY_DOCUMENT + override fun wrapData(data: Data?): Data? = data + override fun variables(): Operation.Variables = 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 + ) + + interface HeroCharacter { + fun marshaller(): ResponseFieldMarshaller + } + + /** + * A humanoid creature from the Star Wars universe + */ + data class AsHuman( + val __typename: String = "Human", + /** + * What this human calls themselves + */ + val name: String?, + /** + * Height in the preferred unit, default is meters + */ + val height: Double? + ) : HeroCharacter { + override fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@AsHuman.__typename) + writer.writeString(RESPONSE_FIELDS[1], this@AsHuman.name) + writer.writeDouble(RESPONSE_FIELDS[2], this@AsHuman.height) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forString("name", "name", null, true, listOf( + ResponseField.Condition.booleanCondition("myBool", false) + )), + ResponseField.forDouble("height", "height", mapOf( + "unit" to mapOf( + "kind" to "Variable", + "variableName" to "unit")), true, null) + ) + + operator fun invoke(reader: ResponseReader): AsHuman = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val name = readString(RESPONSE_FIELDS[1]) + val height = readDouble(RESPONSE_FIELDS[2]) + AsHuman( + __typename = __typename, + name = name, + height = height + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + /** + * A character from the Star Wars universe + */ + data class Hero( + val __typename: String = "Character", + /** + * The name of the character + */ + val name: String?, + val asHuman: AsHuman? + ) { + fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@Hero.__typename) + writer.writeString(RESPONSE_FIELDS[1], this@Hero.name) + writer.writeFragment(this@Hero.asHuman?.marshaller()) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forString("name", "name", null, true, listOf( + ResponseField.Condition.booleanCondition("myBool", false) + )), + ResponseField.forFragment("__typename", "__typename", listOf( + ResponseField.Condition.typeCondition(arrayOf("Human")) + )) + ) + + operator fun invoke(reader: ResponseReader): Hero = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val name = readString(RESPONSE_FIELDS[1]) + val asHuman = readFragment(RESPONSE_FIELDS[2]) { reader -> + AsHuman(reader) + } + Hero( + __typename = __typename, + name = name, + asHuman = asHuman + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + /** + * A humanoid creature from the Star Wars universe + */ + data class HeroWithReview( + val __typename: String = "Human", + /** + * What this human calls themselves + */ + val name: String + ) { + fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeString(RESPONSE_FIELDS[0], this@HeroWithReview.__typename) + writer.writeString(RESPONSE_FIELDS[1], this@HeroWithReview.name) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forString("__typename", "__typename", null, false, null), + ResponseField.forString("name", "name", null, false, null) + ) + + operator fun invoke(reader: ResponseReader): HeroWithReview = reader.run { + val __typename = readString(RESPONSE_FIELDS[0])!! + val name = readString(RESPONSE_FIELDS[1])!! + HeroWithReview( + __typename = __typename, + name = name + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + /** + * Data from the response after executing this GraphQL operation + */ + data class Data( + val hero: Hero?, + val heroWithReview: HeroWithReview? + ) : Operation.Data { + override fun marshaller(): ResponseFieldMarshaller = ResponseFieldMarshaller.invoke { writer -> + writer.writeObject(RESPONSE_FIELDS[0], this@Data.hero?.marshaller()) + writer.writeObject(RESPONSE_FIELDS[1], this@Data.heroWithReview?.marshaller()) + } + + companion object { + private val RESPONSE_FIELDS: Array = arrayOf( + ResponseField.forObject("hero", "hero", null, true, null), + ResponseField.forObject("heroWithReview", "heroWithReview", mapOf( + "listOfInts" to mapOf( + "kind" to "Variable", + "variableName" to "listOfInts")), true, null) + ) + + operator fun invoke(reader: ResponseReader): Data = reader.run { + val hero = readObject(RESPONSE_FIELDS[0]) { reader -> + Hero(reader) + } + val heroWithReview = readObject(RESPONSE_FIELDS[1]) { reader -> + HeroWithReview(reader) + } + Data( + hero = hero, + heroWithReview = heroWithReview + ) + } + + @Suppress("FunctionName") + fun Mapper(): ResponseFieldMapper = ResponseFieldMapper { invoke(it) } + } + } + + companion object { + const val OPERATION_ID: String = + "174c448da54ce10fe5ade777417fe38dcbc242e68cd54f905a7c6e465e02fdf1" + + val QUERY_DOCUMENT: String = QueryDocumentMinifier.minify( + """ + |query GetHero(${'$'}myBool: Boolean = true, ${'$'}unit: LengthUnit! = FOOT, ${'$'}listOfInts: [Int] = [1, 2, 3]) { + | hero { + | __typename + | name @include(if: ${'$'}myBool) + | ... on Human { + | height(unit: ${'$'}unit) + | } + | } + | heroWithReview(listOfInts: ${'$'}listOfInts) { + | __typename + | name + | } + |} + """.trimMargin() + ) + + val OPERATION_NAME: OperationName = object : OperationName { + override fun name(): String = "GetHero" + } + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/TestOperation.graphql b/apollo-compiler/src/test/graphql/com/example/variable_default_value/TestOperation.graphql new file mode 100644 index 00000000000..47e31c9412a --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/TestOperation.graphql @@ -0,0 +1,27 @@ +query GetHero( + $myBool: Boolean = true, + $unit: LengthUnit! = FOOT, + $listOfInts: [Int] = [1, 2, 3] + # FIXME + #$review: ReviewInput! = { + # stars: 0, + # favoriteColor: { + # red: 1, + # blue: 1 + # }, + # listOfStringNonOptional: ["foo"], + #} +) { + hero { + name @include(if: $myBool) + ... on Human { + height(unit: $unit) + } + } + heroWithReview(listOfInts: $listOfInts) { + name + } + #heroWithReview(review: $review) { + # name + #} +} diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/CustomType.java b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/CustomType.java new file mode 100644 index 00000000000..ed782d46309 --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/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.variable_default_value.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/variable_default_value/type/CustomType.kt b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/CustomType.kt new file mode 100644 index 00000000000..ff18842a97f --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/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.variable_default_value.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" + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.java b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.java new file mode 100644 index 00000000000..13e958fd94a --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.java @@ -0,0 +1,47 @@ +// 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.variable_default_value.type; + +import java.lang.String; + +/** + * Units of height + */ +public enum LengthUnit { + /** + * The standard unit around the world + */ + METER("METER"), + + /** + * Primarily used in the United States + */ + FOOT("FOOT"), + + /** + * Auto generated constant for unknown enum values + */ + $UNKNOWN("$UNKNOWN"); + + private final String rawValue; + + LengthUnit(String rawValue) { + this.rawValue = rawValue; + } + + public String rawValue() { + return rawValue; + } + + public static LengthUnit safeValueOf(String rawValue) { + for (LengthUnit enumValue : values()) { + if (enumValue.rawValue.equals(rawValue)) { + return enumValue; + } + } + return LengthUnit.$UNKNOWN; + } +} diff --git a/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.kt b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.kt new file mode 100644 index 00000000000..9a583338ebf --- /dev/null +++ b/apollo-compiler/src/test/graphql/com/example/variable_default_value/type/LengthUnit.kt @@ -0,0 +1,35 @@ +// 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.variable_default_value.type + +import kotlin.String + +/** + * Units of height + */ +enum class LengthUnit( + val rawValue: String +) { + /** + * The standard unit around the world + */ + METER("METER"), + + /** + * Primarily used in the United States + */ + FOOT("FOOT"), + + /** + * Auto generated constant for unknown enum values + */ + UNKNOWN__("UNKNOWN__"); + + companion object { + fun safeValueOf(rawValue: String): LengthUnit = values().find { it.rawValue == rawValue } ?: + UNKNOWN__ + } +}