apollographql / apollo-kotlin

:rocket:  A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
https://www.apollographql.com/docs/kotlin
MIT License
3.76k stars 653 forks source link

generated classes not importing input types for array arguments #1172

Closed wawhal closed 5 years ago

wawhal commented 5 years ago

Using version (1.0.0-alpha4)

If the graphql mutation has an array argument, the associated type is not imported in the generated class.

Related details:

  1. schema.json

  2. The mutation is:

    mutation Some ($usersinput:[UsersInput]){
      addUsers (
        users: $usersinput
      ) {
        id
        name
      }
    }
  3. The generated class is:

    Click to expand ```java import com.apollographql.apollo.api.Input; import com.apollographql.apollo.api.InputFieldMarshaller; import com.apollographql.apollo.api.InputFieldWriter; import com.apollographql.apollo.api.Mutation; import com.apollographql.apollo.api.Operation; import com.apollographql.apollo.api.OperationName; import com.apollographql.apollo.api.ResponseField; import com.apollographql.apollo.api.ResponseFieldMapper; import com.apollographql.apollo.api.ResponseFieldMarshaller; import com.apollographql.apollo.api.ResponseReader; import com.apollographql.apollo.api.ResponseWriter; import com.apollographql.apollo.api.internal.UnmodifiableMapBuilder; import com.apollographql.apollo.api.internal.Utils; import java.io.IOException; import java.lang.Object; import java.lang.Override; import java.lang.String; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Generated; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; // UsersInput has not been imported (The type has been generated) // import type.UsersInput; @Generated("Apollo GraphQL") public final class SomeMutation implements Mutation { public static final String OPERATION_ID = "5e621ce2d6ad52ad8438102c751de59ddc58519d8acf3da2cf156e62fe783754"; public static final String QUERY_DOCUMENT = "mutation Some($usersinput: [UsersInput]) {\n" + " addUsers(users: $usersinput) {\n" + " __typename\n" + " id\n" + " name\n" + " }\n" + "}"; public static final OperationName OPERATION_NAME = new OperationName() { @Override public String name() { return "Some"; } }; private final SomeMutation.Variables variables; public SomeMutation(@NotNull Input> usersinput) { Utils.checkNotNull(usersinput, "usersinput == null"); variables = new SomeMutation.Variables(usersinput); } @Override public String operationId() { return OPERATION_ID; } @Override public String queryDocument() { return QUERY_DOCUMENT; } @Override public SomeMutation.Data wrapData(SomeMutation.Data data) { return data; } @Override public SomeMutation.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; } public static final class Builder { private Input> usersinput = Input.absent(); Builder() { } public Builder usersinput(@Nullable List usersinput) { this.usersinput = Input.fromNullable(usersinput); return this; } public Builder usersinputInput(@NotNull Input> usersinput) { this.usersinput = Utils.checkNotNull(usersinput, "usersinput == null"); return this; } public SomeMutation build() { return new SomeMutation(usersinput); } } public static final class Variables extends Operation.Variables { private final Input> usersinput; private final transient Map valueMap = new LinkedHashMap<>(); Variables(Input> usersinput) { this.usersinput = usersinput; if (usersinput.defined) { this.valueMap.put("usersinput", usersinput.value); } } public Input> usersinput() { return usersinput; } @Override public Map valueMap() { return Collections.unmodifiableMap(valueMap); } @Override public InputFieldMarshaller marshaller() { return new InputFieldMarshaller() { @Override public void marshal(InputFieldWriter writer) throws IOException { if (usersinput.defined) { writer.writeList("usersinput", usersinput.value != null ? new InputFieldWriter.ListWriter() { @Override public void write(InputFieldWriter.ListItemWriter listItemWriter) throws IOException { for (final UsersInput $item : usersinput.value) { listItemWriter.writeObject($item != null ? $item.marshaller() : null); } } } : null); } } }; } } public static class Data implements Operation.Data { static final ResponseField[] $responseFields = { ResponseField.forList("addUsers", "addUsers", new UnmodifiableMapBuilder(1) .put("users", new UnmodifiableMapBuilder(2) .put("kind", "Variable") .put("variableName", "usersinput") .build()) .build(), true, Collections.emptyList()) }; final @Nullable List addUsers; private transient volatile String $toString; private transient volatile int $hashCode; private transient volatile boolean $hashCodeMemoized; public Data(@Nullable List addUsers) { this.addUsers = addUsers; } public @Nullable List addUsers() { return this.addUsers; } public ResponseFieldMarshaller marshaller() { return new ResponseFieldMarshaller() { @Override public void marshal(ResponseWriter writer) { writer.writeList($responseFields[0], addUsers, new ResponseWriter.ListWriter() { @Override public void write(List items, ResponseWriter.ListItemWriter listItemWriter) { for (Object item : items) { listItemWriter.writeObject(((AddUser) item).marshaller()); } } }); } }; } @Override public String toString() { if ($toString == null) { $toString = "Data{" + "addUsers=" + addUsers + "}"; } return $toString; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof Data) { Data that = (Data) o; return ((this.addUsers == null) ? (that.addUsers == null) : this.addUsers.equals(that.addUsers)); } return false; } @Override public int hashCode() { if (!$hashCodeMemoized) { int h = 1; h *= 1000003; h ^= (addUsers == null) ? 0 : addUsers.hashCode(); $hashCode = h; $hashCodeMemoized = true; } return $hashCode; } public static final class Mapper implements ResponseFieldMapper { final AddUser.Mapper addUserFieldMapper = new AddUser.Mapper(); @Override public Data map(ResponseReader reader) { final List addUsers = reader.readList($responseFields[0], new ResponseReader.ListReader() { @Override public AddUser read(ResponseReader.ListItemReader listItemReader) { return listItemReader.readObject(new ResponseReader.ObjectReader() { @Override public AddUser read(ResponseReader reader) { return addUserFieldMapper.map(reader); } }); } }); return new Data(addUsers); } } } public static class AddUser { static final ResponseField[] $responseFields = { ResponseField.forString("__typename", "__typename", null, false, Collections.emptyList()), ResponseField.forInt("id", "id", null, false, Collections.emptyList()), ResponseField.forString("name", "name", null, true, Collections.emptyList()) }; final @NotNull String __typename; final int id; final @Nullable String name; private transient volatile String $toString; private transient volatile int $hashCode; private transient volatile boolean $hashCodeMemoized; public AddUser(@NotNull String __typename, int id, @Nullable String name) { this.__typename = Utils.checkNotNull(__typename, "__typename == null"); this.id = id; this.name = name; } public @NotNull String __typename() { return this.__typename; } public int id() { return this.id; } public @Nullable String name() { return this.name; } public ResponseFieldMarshaller marshaller() { return new ResponseFieldMarshaller() { @Override public void marshal(ResponseWriter writer) { writer.writeString($responseFields[0], __typename); writer.writeInt($responseFields[1], id); writer.writeString($responseFields[2], name); } }; } @Override public String toString() { if ($toString == null) { $toString = "AddUser{" + "__typename=" + __typename + ", " + "id=" + id + ", " + "name=" + name + "}"; } return $toString; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof AddUser) { AddUser that = (AddUser) o; return this.__typename.equals(that.__typename) && this.id == that.id && ((this.name == null) ? (that.name == null) : this.name.equals(that.name)); } return false; } @Override public int hashCode() { if (!$hashCodeMemoized) { int h = 1; h *= 1000003; h ^= __typename.hashCode(); h *= 1000003; h ^= id; h *= 1000003; h ^= (name == null) ? 0 : name.hashCode(); $hashCode = h; $hashCodeMemoized = true; } return $hashCode; } public static final class Mapper implements ResponseFieldMapper { @Override public AddUser map(ResponseReader reader) { final String __typename = reader.readString($responseFields[0]); final int id = reader.readInt($responseFields[1]); final String name = reader.readString($responseFields[2]); return new AddUser(__typename, id, name); } } } } ```
  4. Whenever I compile, I get this error:

    error: cannot find symbol class UsersInput

GraphQL server and android code to reproduce the issue : https://github.com/wawhal/test-apollo-android/

Edit

Workaround: Move the .graphql files and the schema.json to a subfolder like com/org/appname. The working example app is pushed to a branch called working in wawhal/test-apollo-android

sav007 commented 5 years ago

Try to put all graph files under some sub folders, not in root of /graphql folder. For example put them under /graphql/com/packagename/...

wawhal commented 5 years ago

@sav007 that does not work. Same issue.

sav007 commented 5 years ago

TBH not sure why you see this issue, just checked on the our tests, and it works fine. So just to make sure:

worm69 commented 5 years ago

Hi i feel same isse. If need i can provide my project to test @sav007

alexandru-calinoiu commented 5 years ago

I have the same issue with alpha4, my array type is an enum and it's a query not a mutation

query Play($id: String!, $context: String, $supported_actions: [PlayFlowActionEnum]!) {
alexandru-calinoiu commented 5 years ago

Same issue with 1.0.1-SNAPSHOT

sav007 commented 5 years ago

@worm69 that would be super helpful. If someone provides me a sample how to reproduce this issue, I will appreciate.

wawhal commented 5 years ago

@sav007 my apologies. The solution that you mentioned seems to work:

Try to put all graphl files under some sub folders, not in root of /graphql folder. For example put them under /graphql/com/packagename/...

Any reason this placement in subfolders is enforced? The docs say that placing the .graphql files inside main/graphql should work.

worm69 commented 5 years ago

@sav007 thx i hope help find bug mobiletaiga-dev.zip

sav007 commented 5 years ago

Thx will take a look today

sav007 commented 5 years ago

Sorry for the delay just got some free time. By quick looking into the code:

screen shot 2019-01-27 at 9 46 26 am

As I recommended try to put schema and graphql files under some subfolder:

screen shot 2019-01-27 at 9 48 54 am
worm69 commented 5 years ago

@sav007 No Problem. Thx for help me on your free time 👌👏

sav007 commented 5 years ago

Closing