graphql-java-codegen
                                
                                 graphql-java-codegen copied to clipboard
                                
                                    graphql-java-codegen copied to clipboard
                            
                            
                            
                        GraphQL Schema with multi-level operations fails to create GraphQLOperationRequest
Issue Description
I have a schema like below
type Query {
    product: productQuery
}
type Mutation {
    product: productMutation
}
type productQuery  {
    productOrganization(id: ID!): productOrganization
}
type productOrganization {
    id: ID!
    enabled: Boolean!
    createdOn: DateTime!
    updatedOn: DateTime
}
Now when I use the codegen tool to generate models
the productQueryRequest implements GraphQLOperationRequest{} is generated without any params to link to productOrganization . as shown below
public class productQueryRequest implements GraphQLOperationRequest {
    public static final String OPERATION_NAME = "product";
    public static final GraphQLOperation OPERATION_TYPE = GraphQLOperation.QUERY;
    private String alias;
    private final Map<String, Object> input = new LinkedHashMap<>();
    private final Set<String> useObjectMapperForInputSerialization = new HashSet<>();
    public productQueryRequest() {
    }
    public productQueryRequest(String alias) {
        this.alias = alias;
    }
}
The reason that I understand is the leveling I have in my query (3 levels) ; like query -> productQuery -> productOrganization has this been 2 levels it would have been fine like if query-> productOrganization.
Steps to Reproduce
List in detail the exact steps to reproduce the unexpected behavior.
Run the codegen on schema above
Expected Result
Explain in detail what behavior you expected to happen. I would expect productQuery in this case to have an injection for productOrganization in the model generated
Actual Result
Described above
Your Environment and Setup
- graphql-java-codegen version: 5.4.0
- Build tool: Maven
<plugin>
                <groupId>io.github.kobylynskyi</groupId>
                <artifactId>graphql-codegen-maven-plugin</artifactId>
                <version>${codegen.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <graphqlSchemaPaths>
                                <graphqlSchemaPath>${project.basedir}/src/main/resources/sources/schema.graphqls</graphqlSchemaPath>
                             </graphqlSchemaPaths>
                            <outputDir>${project.build.directory}/generated-sources/src/main/java</outputDir>
                            <packageName>com.product.schema</packageName>
                            <fieldsWithResolvers>true</fieldsWithResolvers>
                            <generateClient>true</generateClient>
                            <generateApis>false</generateApis>
                            <responseProjectionMaxDepth>10</responseProjectionMaxDepth>
<!--                            <generateParameterizedFieldsResolvers>true</generateParameterizedFieldsResolvers>-->
                            <generateEqualsAndHashCode>true</generateEqualsAndHashCode>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
@vpandey3 please provide the code that you expect to be generated
Hello , Thanks for looking into this
expected -
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperationRequest;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@javax.annotation.processing.Generated(
    value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen",
    date = "2022-03-01T13:46:37-0600"
)
public class ProductQueryRequest implements GraphQLOperationRequest {
    public static final String OPERATION_NAME = "Product";
    public static final GraphQLOperation OPERATION_TYPE = GraphQLOperation.QUERY;
    private String alias;
    private final Map<String, Object> input = new LinkedHashMap<>();
    private final Set<String> useObjectMapperForInputSerialization = new HashSet<>();
    public ProductQueryRequest() {
    }
    public ProductQueryRequest(String alias) {
        this.alias = alias;
    }
    public void setProductOrganization(ProductQueryProductOrganizationParametrizedInput input) {
        this.input.put("ProductOrganization", input);
    }
    @Override
    public GraphQLOperation getOperationType() {
        return OPERATION_TYPE;
    }
    @Override
    public String getOperationName() {
        return OPERATION_NAME;
    }
    @Override
    public String getAlias() {
        return alias != null ? alias : OPERATION_NAME;
    }
    @Override
    public Map<String, Object> getInput() {
        return input;
    }
    @Override
    public Set<String> getUseObjectMapperForInputSerialization() {
        return useObjectMapperForInputSerialization;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final ProductQueryRequest that = (ProductQueryRequest) obj;
        return Objects.equals(getOperationType(), that.getOperationType()) &&
                   Objects.equals(getOperationName(), that.getOperationName()) &&
                   Objects.equals(input, that.input);
    }
    @Override
    public int hashCode() {
        return Objects.hash(getOperationType(), getOperationName(), input);
    }
    @Override
    public String toString() {
        return Objects.toString(input);
    }
    public static ProductQueryRequest.Builder builder() {
        return new ProductQueryRequest.Builder();
    }
    public static class Builder {
        private String $alias;
        private ProductQueryProductOrganizationParametrizedInput orgInput;
        public Builder() {
        }
        public Builder alias(String alias) {
            this.$alias = alias;
            return this;
        }
        public Builder setId(ProductQueryProductOrganizationParametrizedInput input) {
            this.orgInput = input;
            return this;
        }
        public ProductQueryRequest build() {
            ProductQueryRequest obj = new ProductQueryRequest($alias);
            obj.setProductOrganization(this.orgInput);
            return obj;
        }
    }
}
generated
package com.atlassian.Product.streams.model.schema;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperationRequest;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@javax.annotation.processing.Generated(
    value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen",
    date = "2022-03-01T13:46:37-0600"
)
public class ProductQueryRequest implements GraphQLOperationRequest {
    public static final String OPERATION_NAME = "Product";
    public static final GraphQLOperation OPERATION_TYPE = GraphQLOperation.QUERY;
    private String alias;
    private final Map<String, Object> input = new LinkedHashMap<>();
    private final Set<String> useObjectMapperForInputSerialization = new HashSet<>();
    public ProductQueryRequest() {
    }
    public ProductQueryRequest(String alias) {
        this.alias = alias;
    }
    @Override
    public GraphQLOperation getOperationType() {
        return OPERATION_TYPE;
    }
    @Override
    public String getOperationName() {
        return OPERATION_NAME;
    }
    @Override
    public String getAlias() {
        return alias != null ? alias : OPERATION_NAME;
    }
    @Override
    public Map<String, Object> getInput() {
        return input;
    }
    @Override
    public Set<String> getUseObjectMapperForInputSerialization() {
        return useObjectMapperForInputSerialization;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final ProductQueryRequest that = (ProductQueryRequest) obj;
        return Objects.equals(getOperationType(), that.getOperationType()) &&
                   Objects.equals(getOperationName(), that.getOperationName()) &&
                   Objects.equals(input, that.input);
    }
    @Override
    public int hashCode() {
        return Objects.hash(getOperationType(), getOperationName(), input);
    }
    @Override
    public String toString() {
        return Objects.toString(input);
    }
    public static ProductQueryRequest.Builder builder() {
        return new ProductQueryRequest.Builder();
    }
    public static class Builder {
        private String $alias;
        public Builder() {
        }
        public Builder alias(String alias) {
            this.$alias = alias;
            return this;
        }
        public ProductQueryRequest build() {
            ProductQueryRequest obj = new ProductQueryRequest($alias);
            return obj;
        }
    }
}
Diff is - the GraphQLOperationRequest generated type which is ProductQueryRequest does not provide any way to link the ProductQueryProductOrganization sub query hence I cant query for that.
Thanks for providing the info. The thing is that you are using query response type as an input, so it is considered as the parametrized input. Try disabling the following property: generateParameterizedFieldsResolvers = false
already tried with generateParameterizedFieldsResolvers = false ; same issue :(
query response type as an input not sure if I am following this comment? what response type am I using?
Overall your schema design looks not as per GraphQL best-practices. I think the better solution would be:
type Query {
    productOrganization(id: ID!): ProductOrganization
}
type ProductOrganization {
    id: ID!
    enabled: Boolean!
    createdOn: DateTime!
    updatedOn: DateTime
}
+1 to this. We really really want to use this library but without this we can't. We don't want to follow the way that is listed because we need to follow nesting practices to keep our queries separate, eg: they may come from different sources.I'm not sure I'd consider it against best practices since isn't the strong suite of graphql that queries can be nested? Either for this or for having fields that can take args and so on.
Any idea how much work this would be? Would possibly be able to help contribute.
Thanks for creating this library!