Coder Social home page Coder Social logo

kobylynskyi / graphql-java-codegen Goto Github PK

View Code? Open in Web Editor NEW
257.0 8.0 112.0 3.2 MB

Make your GraphQL Java application schema-driven.

Home Page: https://kobylynskyi.github.io/graphql-java-codegen/

License: MIT License

Java 86.82% FreeMarker 7.31% Scala 5.19% Kotlin 0.40% Shell 0.28%
graphql-java graphql graphql-codegen codegen graphql-maven graphql-gradle graphql-kotlin graphql-scala

graphql-java-codegen's Introduction

GraphQL Codegen

Donate

Build Discussions License: MIT

Sonarcloud Status SonarCloud Coverage SonarCloud Bugs SonarCloud Vulnerabilities

GraphQL Java Codegen makes it easy to make your Java application to follow a schema-first approach whether it is a server or client application.

Following classes can be generated based on your GraphQL schema:

  • Interfaces for GraphQL queries, mutations and subscriptions.
  • Interfaces for GraphQL unions.
  • POJO classes for GraphQL types and inputs.
  • Enum classes for GraphQL enums.
  • Interfaces for GraphQL type fields (e.g. for parametrized fields) aka "Resolvers".
  • Client Request classes for GraphQL queries, mutations and subscriptions.

Features

  • Generate classes in Java, Kotlin or Scala.
  • Recursive schemas lookup by file name pattern.
  • Generate code based on GraphQL schema or GraphQL Query Introspection Result.
  • Generate POJOs with or without: Builder pattern, immutable fields, toString(), equals() and hashCode(), etc.
  • Flexible API interfaces naming conventions (based on schema file name, folder name, etc.)
  • Custom java package names for model and API classes.
  • Custom prefix/suffix for model, API, type resolver, request, response classes.
  • Custom annotations for generated classes (e.g.: validation annotations for generated model classes or specific type fields, annotations for GraphQL directives, etc.)
  • Relay support.
  • Ability to define codegen configuration via external json file.

For the full list of codegen configs please refer to: Codegen Options

Supported plugins

Contributing

Please see CONTRIBUTING.md.

Inspired by

swagger-codegen

graphql-java-codegen's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-java-codegen's Issues

Subscription methods with wrong return type

Having a subscription in the schema generates a Subscription class with methods that have the wrong return type. Subscriptions have to return org.reactivestreams.Publisher<T>.

Given schema:

schema {
  subscription: Subscription
}
type Subscription {
 projectsSub: Project!
}
type Project {
  id: String!
  name: String!
}

Generated class:

public interface ProjectsSubSubscription {
    @javax.validation.constraints.NotNull
    Project projectsSub() throws Exception;
}

Expected:

public interface ProjectsSubSubscription {
    @javax.validation.constraints.NotNull
    org.reactivestreams.Publisher<Project> projectsSub() throws Exception;
}

hierarchical organisation of models / apis

Hi, not sure if this is related to the support for extensions (#81), but as our graphql schemas grow and we are using different files to maintain some organisation, would it be possible to have some form of organisation for the generated model and API files as well? Just a little thing as my model and api (especially api) folders are probably going to grow drastically, so would like to organise the files inside. Thank you!

Configs in "graphqlSchemas" should be optional

If only graphqlSchemaPaths is specified then the build gradle task fails.

Gradle config:

graphqlCodegen {
    graphqlSchemaPaths = ["$projectDir/src/main/resources/schema.graphqls".toString()]
    outputDir = new File("$buildDir/generated")
}

Output:

Bogdans-MacBook-Pro:gradle bogdankobylynskyi$ ./gradlew clean build publishToMavenLocal
> Task :example:graphqlCodegen FAILED

FAILURE: Build failed with an exception.

* What went wrong:
A problem was found with the configuration of task ':example:graphqlCodegen'.
> No value has been specified for property 'graphqlSchemas.rootDir'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
3 actionable tasks: 3 executed

Using the client code generator with RESTAssured

Hi @kobylynskyi @joffrey-bion, I've seen the client code working now with the Spring rest template so all good there. Just wondered if this would also work with RESTAssured for BDD testing? RESTAssured gives that standard Given When Then BDD structure and would also be useful to be able to hook the client code generated by your plugin into that. In your example, you build the request with a projection:-

    GraphQLRequest request = new GraphQLRequest(getProductRequest,
            new ProductResponseProjection()
                    .id()
                    .title()
                    .price());

.. and then use the Spring RestTemplate to issue the request using a ParameterizedTypeReference for the response. As well as be able to issue the request with the Spring Rest template, it would be useful to see how that may hook into a RESTAssured call in the structure :-

 response = request
        .given()
        .headers(headers)
        .body(payload)
        .contentType(ContentType.JSON)
        .when()
        .post(url)
        .then()
        .statusCode(status)
        .extract()
        .response();

Not sure if this is possible with RESTAssured - but ultimately all its doing is invoking a REST service, so if you have any ideas, then would be useful to hear what you think.

Setting default values for query params [Question]

Hi, how can I set the default values for query params? In my graphql schema, I'm writing something like:

getCurrentAlerts(size: Int=10, skip: Int=0, showAll: Boolean=false): [Alert!]!

But in the generated file, I'm simply getting:

public interface GetCurrentAlertsQuery { @javax.validation.constraints.NotNull Collection<Alert> getCurrentAlerts(Integer size, Integer skip, Boolean showAll) throws Exception; }

Thanks!

how do I actually get started? (Version: [graphql-java-codegen 1.6.0])

Hi, dumb question here but how do I actually use this? I'm using Eclipse for Java and using gradle, the relevant build.gradle is as follows:

`plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
// schema-driven graphql
id "io.github.kobylynskyi.graphql.codegen" version "1.6.0"
}

/**

  • Generate requests and model from external service
    */
    // Automatically generate GraphQL code on project build:
    compileJava.dependsOn "graphqlCodegen"
    // Add generated sources to your project source sets:
    sourceSets.main.java.srcDir "$buildDir/generated"
    graphqlCodegen {
    graphqlSchemaPaths = [
    "$projectDir/src/main/resources/graphql/XXX.graphqls".toString(),
    "$projectDir/src/main/resources/graphql/YYY.graphqls".toString(),
    "$projectDir/src/main/resources/graphql/ZZZ.graphqls".toString()
    ]
    outputDir = new File("$buildDir/generated")
    apiPackageName = "aries.graphql.api"
    modelPackageName = "aries.graphql.model"
    generateApis = true
    }`

However, I am not able to import anything from aries.graphql, and hence nothing from aries.graphql.api or aries.graphql.model. Do I need to run gradle build first, and if so how do I reference the model files, since they will be only in the build dir? Apologies for the dumb question as I am not very able to understand the examples as well. I have also looked through the issues and don't really see anyone else struggling with this.

Thanks a lot in advance!

Implement while keeping DataFetcher [Question]

Based on my understanding, the APIs generated (query etc.) should be implemented in the desired format, and it returns the output format of the query. I have legacy code that uses (for example) the Java library graphql.schema.DataFetcher to return the output which is then processed and exposed as the relevant endpoint.

Is it possible to reuse this DataFetcher, or will I have to completely rewrite the code (not recommended as many others are still using it)?

If the latter case, what is the recommended way of implementing the endpoint? I have seen the Controller in "https://github.com/kobylynskyi/graphql-java-codegen/blob/master/plugins/gradle/example-client/src/main/java/io/github/kobylynskyi/order/graphql/GraphQLController.java", but I'm not sure how to connect this with my schema and implementation.

Thanks!

Client-side code generation

Is your feature request related to a problem? Please describe.
Currently, the main purpose of the plugin is to generate server-side code (POJOs and interfaces).
The idea is to add support of client-side classes generation. This will allow users to build a GraphQL request.

Describe the solution you'd like
Schema example:

type Mutation {
    newBike(bikeInput: BikeInput!): Bike
}

input BikeInput {
    type: BikeType!
    brand: String!
    size: String!
    price: BigDecimal
}

type Bike {
    id: ID
    type: BikeType
    brand: String
    size: String
    price: BigDecimal
    addedDateTime: DateTime
}

Generated class:

public class NewBikeMutationRequest {
    private BikeInput bikeInput;
    //getters/setters
}

public class BikeInputTO {
    private BikeTypeTO type;
    private String brand;
    private String size;
    private String price;

    public BikeInputTO() { }

    //getters/setters

The end-user will then use these classes to build the request and response projection (open question for now). So that it will be serialized to the following request string:

mutation newBike {
  newBike(bike: {
    type: ROAD,
    brand: "LOL",
    size: "Big",
    year: 2019,
    price: "123.45"
  }) {
    id
    addedDateTime
  }
}

Ability to process multiple graphql files to separate source sets

Is your feature request related to a problem? Please describe.
If 2 GraphQL schemas have similar types and I try to register 2 schemas as part of
graphqlSchemaPaths = listOf("path_to_graphql_schema1", "path_to_graphql_schema2")
the task gives me an error that file names clash.
My use case is to generate model objects for the GraphQL gateway, which has multiple GraphQL services under it.

Describe the solution you'd like
The solution is

tasks.named<GraphqlCodegenGradleTask>("graphqlCodegen") {
    class GraphQLCodegenConfig(var graphqlSchemaPaths: String, var outputDir: File, var packageName: String)

    var graphQLCodegenConfig: GraphQLCodegenConfig? = null
    val service = project.property("service")
    println(service)
    graphQLCodegenConfig = when(service) {
        "example1" -> GraphQLCodegenConfig(
            graphqlSchemaPaths = "$projectDir/src/main/resources/graphql/example1.graphqls",
            outputDir = File("$buildDir/generated/example1"),
            packageName = "com.example.project.example1")
        "example2" -> GraphQLCodegenConfig(
            graphqlSchemaPaths = "$projectDir/scripts/schema-fetcher/example2.graphql",
            outputDir = File("$buildDir/generated/example2"),
            packageName = "com.example.project.example2")
        else -> GraphQLCodegenConfig(
            graphqlSchemaPaths = "$projectDir/src/main/resources/graphql/example1.graphqls",
            outputDir = File("$buildDir/generated/example1"),
            packageName = "com.example.project.example1")
    }

    graphqlSchemaPaths = listOf(graphQLCodegenConfig!!.graphqlSchemaPaths)
    outputDir = graphQLCodegenConfig.outputDir
    packageName = graphQLCodegenConfig.packageName
    customTypesMapping = mutableMapOf(Pair("EpochMillis", "java.time.LocalDateTime"))
}

sourceSets {
    getByName("main").java.srcDirs("$buildDir/generated/example1")
    getByName("main").java.srcDirs("$buildDir/generated/example2")
}

so client can use command line variable

./gradlew graphqlCodegen -Pservice=example1

or

./gradlew graphqlCodegen -Pservice=example2

GraphQL Float is supposed to be double-precision

Describe the bug
By default, GraphQL Float type result in a Float in Java.
However, the specification states that Float in GraphQL is a double-precision floating point value, and should therefore be a Double instead (at least by default).

Expected behavior
The property type should be Double in Java for a GraphQL field declared as Float.

Steps to reproduce
Use this schema:

type MyType {
    someFloat: Float
}

classes for Query and Mutation not being generated together

For my schemas, I have a root file containing a root Query, Mutation and Subscription:

Query { root : String }

then I have extend Query etc. in later files, all of which (in order, root file first) I am feeding into the build.gradle's graphqlSchemaPaths. However, when I define extend Query and extend Mutation, one of these is not being generated, namely whichever I put second a.k.a. I either only get the Query classes or the Mutation classes generated. This occurs if I put them in the same file or in different files.

I'm also getting errors when I try to gradle build like FileAlreadyExists exception when I have multiple schema files. Do I necessarily need to gradle build when I want to re-generate the classes (e.g. when I change my GraphQL schema), and how do I get around these errors?

Not sure if this is a bug per se, but any help would be much appreciated. Thanks!

Add DataFetchingEnvironment parameter to main query resolver

Is your feature request related to a problem? Please describe.
When using GraphQL Java Tools, the main Query resolver, like other resolvers, can take an optional DataFetchingEnvironment parameter.
It is currently generated in all resolvers interfaces, but not for the root resolvers like Query.

Describe the solution you'd like
It'd be nice to provide this DataFetchingEnvironment argument to the query resolver's methods.
If we need backwards compatibility, we could use a config parameter to add or not the DataFetchingEnvironment argument in the generated interfaces.

Describe alternatives you've considered
There is no other way currently than to stop implementing the Query interface, which is a pain because changes to the schema don't make the compilation fail.

Generate JavaDoc matching the schema comments

Currently, no documentation is generated in the classes even when the schema contains doc comments.
I'm not sure the graphQL parser provides access to comments, but if it does, it'd be great to make them appear as Javadoc.

Support parameterized fields resolvers

Is your feature request related to a problem? Please describe.
In the GraphQL specification, some fields may have arguments:
https://graphql.org/learn/queries/#arguments

This schema for instance:

type Query {
    users: [Person]!
}
type Person {
    name: String!
    height(unit: Unit): Int!
}
enum Unit {
    METERS, FEET
}

Currently, graphql-java-codegen generates a Person class with both name and height fields. However, the height field is parameterized and should be defined in a separate resolver (with the parameters as method arguments).

Describe the solution you'd like
A solution that would match the https://github.com/graphql-java-kickstart/graphql-java-tools approach would be to avoid generating the height field as a field/getter/setter on the Person type, but instead generate a PersonResolver interface with a method to resolve that field, as defined by the GraphQL Java Tools project.

The method should look like:

import graphql.schema.DataFetchingEnvironment;

interface PersonResolver {
    public Integer height(Person person, Unit unit, DataFetchingEnvironment env);
}

Describe alternatives you've considered
Alternatively, or in the meantime, an option to exclude parameterized fields from the generated classes would be a start.

Allow a directory of schemas to be passed in

Is your feature request related to a problem? Please describe.
When we want to break the schema down into many parts (for scalability), it's very inconvenient to have to maintain a list of schemas to process (either in the code or in a gradle/maven plugin).

Describe the solution you'd like
It would be nice to be able to specify a directory so that graphql-java-codegen processes all the schema files inside.

A couple of useful related options to consider:

  • recursive: whether directories should be explored recursively to find schemas
  • schemaFilePattern: a pattern (Java style) to match the schema files to process (would most likely default to .*\.graphqls)
  • excludeFiles: a list of schema files to exclude

Describe alternatives you've considered
The alternatives so far are:

  • to list all files manually (most likely in the maven plugin)
  • to write custom code (maybe as another plugin/task) to build the list of files

ProductServiceGraphQLClient - retrieve list example

Hi @kobylynskyi , in the ProductServiceGraphQLClient class, you have shown the ProductByIdQueryRequest, Could you also share the equivalent implementation for ProductsByIdsQueryRequest? I'm getting Cannot deserialize instance of my class out of START_ARRAY token - I think it's because RestTemplate doesn't handle Lists of objects being returned (https://www.baeldung.com/spring-rest-template-list). I've tried a few things but not getting any success - the GraphQl call is successful , it's just deserialising into the list and I can see your schema - productsByIds(ids: [ID!]!): [Product] returns a list of Products .

Union type members don't implement union interface if declared in separate file

Describe the bug
If the types that are part of a union type are declared in other files than the union itself, they don't implement the union interface in the generated code.

To Reproduce

schema1.graphqls:

type A {
    fieldA: String
}

schema2.graphqls:

type B {
    fieldB: String
}
union U = A | B

This will generate class B implements U but a class A that does not implement U.

Expected behavior
All members of the union type should implement the interface.

I believe this has to do with the schema files being processed independently instead of reading all schema files to populate a common registry and then generating code from the common registry.

Add support for extensions

Is your feature request related to a problem? Please describe.

Type extensions are part of the specification, and should therefore be supported. Type extensions, according to the linked spec, can be of the following types:

  • ScalarTypeExtension (extend scalar X)
  • ObjectTypeExtension (extend type X)
  • InterfaceTypeExtension (extend interface X)
  • UnionTypeExtension (extend union X)
  • EnumTypeExtension (extend enum X)
  • InputObjectTypeExtension (extend input X)

Describe the solution you'd like

When defining type extensions in the SDL schema, they should be taken into account and "merged" with the extended type so that the code generation matches the actual schema.

We could (should?) also provide an option (generateExtensionFieldsResolvers?) to consider all fields from Object Type Extensions as fields with resolvers, because it is likely that these fields require a special resolver implementation with a service call. (Should it be true by default? Maybe not.)

If we do this, maybe we should add a new option to do the opposite of fieldsWithResolvers, like fieldsWithoutResolvers, blacklisting some fields so that they are part of the generated POJO instead of being generated as part of the resolver interface.
The reason why I think we should do this is because if the user decides to generate resolvers for most fields that are part of extensions, but not all, it would be painful to list all of them in fieldsWithResolvers. Instead, we should allow declaring generateExtensionFieldsResolvers to handle the general case, and add some exceptions via fieldsWithoutResolvers.

Describe alternatives you've considered

The alternative (current situation) is to force the user to declare all their types in the same schema, which is not ideal and sometimes impossible (in case of remote schema using federation).

Performance improvement: scan the document once

Is your feature request related to a problem? Please describe.
Currently we are iterating through the whole document multiple times in order to retrieve extensions, implemented interfaces, unions having the particular type, etc.

Describe the solution you'd like
Scan the GraphQL document for definitions once in GraphQLDocumentParser.

Include all args constructor in the model generation

Is your feature request related to a problem? Please describe.
I would like to have all arguments constructor generated alongside with no arguments constructor.

Describe the solution you'd like
Current state:

public class TestObject {

    @javax.validation.constraints.NotNull
    private String field;

    public TestObject() {
    }

    public String getField() {
        return field;
    }
    public void setField(String field) {
        this.field = field;
    }

}

desired state:

public class TestObject {

    @javax.validation.constraints.NotNull
    private String field;

    public TestObject() {
    }

    public TestObject(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }
    public void setField(String field) {
        this.field = field;
    }

}

Multiple issues with FieldResolvers (Version: 1.5.0)

Issue Description

  1. modelNameSuffix and modelNamePrefix are not considered when generating FieldResolver interface which results in build failure.
  2. Gradle plugin ignores some properties:
    0a28021#diff-90e622d874a9fff19c74ff1d1c421f0dR38-R39

Expected Result

public interface ItemResolver {
    ProductTO product(ItemTO itemTO, DataFetchingEnvironment env) throws Exception;
}

Actual Result

public interface ItemResolver {
    ProductTO product(Item item, DataFetchingEnvironment env) throws Exception;
}

Your Environment and Setup

  • graphql-java-codegen: 1.5.0
  • Build tool: Gradle
  • Mapping Config:
task graphqlCodegenOrderService(type: GraphqlCodegenGradleTask) {
    graphqlSchemaPaths = ["$projectDir/src/main/resources/schema.graphqls".toString()]
    outputDir = new File("$buildDir/generated-server")
    apiPackageName = "io.github.kobylynskyi.order.graphql.api"
    modelPackageName = "io.github.kobylynskyi.order.graphql.model"
    customTypesMapping = [
            DateTime: "java.util.Date"
    ]
    modelNameSuffix = "TO"
    fieldsWithResolvers = ["Item.product"]
}

Support for async resolvers

At the moment, we already have the possibility of defining a reactive approach for Subscriptions using the subscriptionReturnType in the configuration.

It would be a great idea to define some configuration property to specify the need for a reactive approach for the rest of the resolvers too. Currently, libraries like graphql-java-spring-webflux or graphql-kickstart-spring-webflux allows us to return CompletableFuture<?> from the resolvers.

What do you think about generating resolvers returning Mono or CompletableFuture in order to get the most out of that facility?

Java-doc warnings in SchemaFinder class

Describe the bug

gradle install
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details
> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar

> Task :javadoc
/home/circleci/repo/graphql-java-codegen/src/main/java/com/kobylynskyi/graphql/codegen/supplier/SchemaFinder.java:46: warning: no @param for recursive
    public void setRecursive(boolean recursive) {
                ^
/home/circleci/repo/graphql-java-codegen/src/main/java/com/kobylynskyi/graphql/codegen/supplier/SchemaFinder.java:56: warning: no @param for includePattern
    public void setIncludePattern(String includePattern) {
                ^
/home/circleci/repo/graphql-java-codegen/src/main/java/com/kobylynskyi/graphql/codegen/supplier/SchemaFinder.java:66: warning: no @param for excludedFiles
    public void setExcludedFiles(Set<String> excludedFiles) {
                ^
3 warnings

> Task :javadocJar
> Task :sourcesJar
> Task :install

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.3/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 9s
7 actionable tasks: 7 executed

To Reproduce
Steps to reproduce the behavior:

  1. Execute './gradlew clean build'

Compile-time warnings in gradle plugin build

$ ./gradlew clean build publishToMavenLocal

> Task :graphql-codegen-gradle-plugin:validateTaskProperties
Task property validation finished with warnings:
  - Warning: Type 'io.github.kobylynskyi.graphql.codegen.gradle.GraphqlCodegenGradleTask': property 'graphqlSchemas.excludedFiles' is not annotated with an input or output annotation.
  - Warning: Type 'io.github.kobylynskyi.graphql.codegen.gradle.GraphqlCodegenGradleTask': property 'graphqlSchemas.includePattern' is not annotated with an input or output annotation.
  - Warning: Type 'io.github.kobylynskyi.graphql.codegen.gradle.GraphqlCodegenGradleTask': property 'graphqlSchemas.recursive' is not annotated with an input or output annotation.
  - Warning: Type 'io.github.kobylynskyi.graphql.codegen.gradle.GraphqlCodegenGradleTask': property 'graphqlSchemas.rootDir' is not annotated with an input or output annotation.

BUILD SUCCESSFUL in 3s
14 actionable tasks: 14 executed

[outputDir missing or invalid] (Version: [graphql-java-codegen 16.0])

Issue Description

Hi, I have configured the plugin in my pom XML file according to the guide. But I get exceptions when
executing the generate goal of the plugin saying outputDir missing or invalid. Could you give some help?

Steps to Reproduce

Then I execute the goal in command line
"mvn graphql-codegen:generate -X"

Expected Result

I expect the generated folder should be created

Actual Result

image

Your Environment and Setup

  • graphql-java-codegen: E.g.: 1.6.0
  • Build tool: E.g.: Maven
  • Java tool: E.g.: openjdk 11
  • Mapping Config: E.g.:
                        <configuration>
                            <graphqlSchemaPaths>${project.basedir}/src/main/resources/schema.graphqls</graphqlSchemaPaths>
                            <outputDir>${project.build.directory}/generated-sources/graphql</outputDir>
                            <packageName>com.qiusuo.springboottutorial.graphql.model</packageName>
                            <customTypesMapping>
                                <DateTime>java.util.Date</DateTime>
                                <Price.amount>java.math.BigDecimal</Price.amount>
                            </customTypesMapping>
                            <modelNameSuffix>TO</modelNameSuffix>
                        </configuration>

Ability to generate only model

Is your feature request related to a problem? Please describe.
Sometimes developers need to generate only model classes (types/enums/inputs/etc.)

Describe the solution you'd like
Extend MappingConfig with 2 new attributes:

  • generateApis - true by default
  • generateModels - true by default

Ability to add custom serialization for scalar types

Is your feature request related to a problem? Please describe.

I have a custom scalar type, which I register as

tasks.named<GraphqlCodegenGradleTask>("graphqlCodegen") {
    ...
    customTypesMapping = mutableMapOf(Pair("EpochMillis", "java.time.LocalDateTime"))
    ...
}

it gives me

public class Comment {
    ...
    private java.time.LocalDateTime createdTimestamp;
    ...

I would like to take a step further and define serialization logic with Jackson for the type

public class Comment {
    ...
    @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.EpochMillisScalarDeserializer.class)
    private java.time.LocalDateTime createdTimestamp;
    ...

where(Kotlin code)

object EpochMillisScalarDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
    override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): LocalDateTime {
        return if (p != null) {
            toLocalDateTime(p.longValue)
        } else {
            LocalDateTime.now()
        }
    }
}

Describe the solution you'd like
I would like to have a configuration

tasks.named<GraphqlCodegenGradleTask>("graphqlCodegen") {
    ...
    customTypesMapping = mutableMapOf(Pair("EpochMillis", "java.time.LocalDateTime"))
    customAnnotationsMapping = mutableMapOf(Pair("EpochMillis", "com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.example.json.EpochMillisScalarDeserializer.class)"))
    ...
}

where custom annotations will be added above fields with the EpochMillis type

Describe alternatives you've considered
Write a custom file editor after the object generation, which is capable of find the right place to insert the desired logic.

Advanced MappingConfig

It would be great to offer the possibility to have an advanced customization of scalar types. For this feature could be a good idea to have something like:

  1. Be able to create custom mappings in an external file, for example a XML file.

  2. Possibility to create rules for a particular input field. For example, if a project has the type "Price" with the field "amount: Float" it can be a good idea the possibility to configure a rule only for this field in order to map this Float to a java.math.BigDecimal.

Improve project structure

Goal:
Improve project structure.

The original request from @joffrey-bion in #41:

The example project can't be built because it is commented out in settings.gradle. However, the plugin's project can't be built the first time if the other one is uncommented, because the dependency is not declared in the build.
Improve the projects setup so that the global build tests everything (including example projects) without having to follow a specific order and without changing directories.

Unify operations and field resolvers data and templates

Conceptually, GraphQL operations (queries/mutations/subscriptions) are a special case of field resolvers that happen to be on well known root types.

Technically, this means that everything about field resolvers is the same as these root types resolvers, apart from the fact that the former get an instance of the resolved type as first param.

Therefore, all the configuration that applies to field resolvers also applies to resolvers of the fields that are part of these root types, like CompletableFuture return type, or DataFetcherEnvironment parameter.

This is why this issue is about unifying the data model and the templates of operations and field resolvers. This will allow to solve both the following issues at the same time:
#61
#63

Generate Builder for Request classes

Currently, all data can be set to the Request class via setters only:

AssignAssetMutationRequest request = new AssignAssetMutationRequest();
request.setAssetId(assetId);
request.setEventId(eventId);

Will be handy to have a Builder for Request class:

AssignAssetMutationRequest request = new AssignAssetMutationRequest.Builder()
      .setAssetId(assetId).setEventId(eventId).build();

Mandatory field annotation does not work for GraphQL input type

GraphQL schema

input BikeInput {
    type: BikeType!
    brand: String!
    size: String!
    year: Int!
    price: BigDecimal
}

Actual Generated class

public class BikeInputTO {

    private BikeTypeTO type;
    private String brand;
    private String size;
    private Integer year;
    private String price;
...
}

Expected generated class

public class BikeInputTO {

    @javax.validation.constraints.NotNull
    private BikeTypeTO type;
    @javax.validation.constraints.NotNull
    private String brand;
    @javax.validation.constraints.NotNull
    private String size;
    @javax.validation.constraints.NotNull
    private Integer year;
    private String price;
...
}

Contributing guidelines incorrect for gradle plugin

cd plugins/graphql-java-codegen-gradle-plugin/graphql-codegen-gradle-plugin
./gradlew clean install

These instructions are unfortunately not possible to follow.

  1. The gradle wrapper is not present in this subfolder (which is ok, but we need to change the command to simply gradle and assume the correct version is installed globally on the machine).
  2. There is no install task on the plugin project:
Task 'install' not found in project ':graphql-codegen-gradle-plugin'.

I guess it should be changed to the publishToMavenLocal task instead.

Last thing, the example project can't be built because it is commented out in settings.gradle.
However, the plugin's project can't be built the first time if the other one is uncommented, because the dependency is not declared in the build.

Maybe we can improve the projects setup so that the global build tests everything (including example projects) without having to follow a specific order and without changing directories.

Define the contract with all configuraiton options

Is your feature request related to a problem? Please describe.
Sometimes it is easy to forget adding the configuration option to Gradle/Maven once it was added to the MappingConfig.
We already had a situation when the new config was added to MappingConfig class and to Gradle plugin task but was not added to maven plugin

Describe the solution you'd like
Create interface GraphQLCodegenConfiguration with all configuration parameters (just getters) and implement this interface in GraphQLCodegenGradleTask and GraphQLCodegenMojo.
In this way we will:

  • Achieve strict contract of the mapping config and will not miss the configuration in plugin configurations nor the configuration type.
  • Avoid duplicating javadoc in all plugins (javadoc will be defined in the interface and inherited to all plugins)

Field resolvers should also be subject to async APIs configuration

Issue Description

The generated field resolvers currently don't respect the generateAsyncApis configuration parameter, although they are part of the API just like top-level resolvers.

Expected Result

When generateAsyncApis is enabled, field resolvers should have a return type wrapped in CompletableFuture as well.

Actual Result

Field resolvers just have a plain java type as return type.

GraphQL interfaces are not generated if mappingConfig has "generateApis = false"

Is your feature request related to a problem? Please describe.
Interfaces generation is not supported as of now.
schema.graphqls

interface MyInterface {
  id: String
  ...
}

There is no class MyInterface automatically generated in the target folder.

Describe the solution you'd like
The interface is automatically generated in the target folder.
MyInterface.java

public interface MyInterface {

	public String getId();
	public void setId(String id);
}

codegen doesn't build consistently

Issue Description

I'm using Eclipse and gradle. Whenever I update my graphql schema, I expect to either:

Option 1

  1. Refresh Gradle Project
  2. Clean project
  3. then Refresh project

or

Option 2

  1. gradle build

to refresh the generated code (classes and interfaces). My issues are that I haven't been able to find a consistent method of re-generating the Java code after updating the schema. Sometimes I do step (1) and it works and other times it doesn't, same for trying step (2), and at the moment neither of these work.

Steps to Reproduce

Can't consistently do it as explained above.

Expected Result

I'm expecting either step (1) or (2) to correctly re-generate the classes, and hence am flagging it as a bug.

Actual Result

What I have found as a workaround is to:

  1. delete the folder with the generated code
  2. run gradle build again
  3. refresh gradle project

and the new code comes up. This seems a little strange, and I'm not expecting to need to delete and rebuild every single time I make some changes in my schema.

If I'm wrong and there's actually a proper way to re-generate the Java code consistently, or if delete + rebuild is actually intended, please point me in the right direction! Thanks :)

Library does not support ! in GraphQL schema during the java classes generation

Describe the bug
During the java classes generation, all of the fields are nullable fields

To Reproduce
Steps to reproduce the behavior:
Run ./gradlew graphqlCodegen
Check the generated classes. Fields have no difference between nullable and not nullable.

Expected behavior
Nullable fields should be assigned null
Not nullable fields should be the same as of now.

Kotlin support

Are there any plans for this tool to support Kotlin class generation?

Plugins never detect absence of schema info

When the user doesn't specify graphqlSchemas nor the explicit paths, the null checks in place don't detect the absence of graphqlSchemas config. The structure is always non null.

We could fix it by adding a check for graphqlSchemas.rootDir instead of a null check.

Another option (which I would prefer) is to define src/main/resources as default rootDir in the plugins (no default in the lib). This is ok because the default include pattern is .*\.graphqls.
This would mean that by default we take all schemas from the resources which seems pretty neat to me.

Support @deprecated directive

If a something is marked with the @deprecated directive in the GraphQL schema, it'd be nice to translate this information into a @Deprecated annotation.

If #84 is implemented, we could also consider generating an @deprecated javadoc tag with the reason provided by the directive.

Gather plugins and core library in same repo?

Having different projects/artifacts for the Maven/Gradle plugins and the core library makes total sense. However, they don't need to be in separate GitHub repositories.

Cross-project changes and releases would be easier if they were in the same git repo:

  • we could make changes to both the plugins and the library in the same commit, always keeping compatibility
  • we wouldn't need to wait for the library release to make changes to the Maven/Gradle plugins
  • we could test the plugins against library changes in the CI of the whole multi-project build

What are your thoughts on this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.