netflix / dgs-codegen Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
According to
If we have scalar LocalDate
in the schema, the generated code will have type java.time.LocalDate
. However graphql-java-extended-scalars
expects java.time.LocalDate
declared as scalar Date
:
Please advise if graphql-java-extended-scalars
is a preferred library for common custom scalar handling. Currently I'm working around it by copying code from it to my classes with names corrected.
Check graphql-java-kickstart
type Query {
users(first: Int, after: String): UserConnection @connection(for: "User")
organizations(first: Int, after: String): OrganizationConnection @connection(for: "Organization")
}
type User {
id: ID!
name: String
}
type Organization {
id: ID!
}
A graphql mutation or query with an argument named input
or operation
will result in a compile error when generating Kotlin.
type Query {
foo(input: String!, operation: String!): Int
}
generateJava {
....
language = "KOTLIN"
generateClient = true // Enable generating the type safe query API
}
Generated code compiles.
Compile errors:
FooGraphqlQuery.kt: 'input' hides member of supertype 'GraphQLQuery' and needs 'override' modifier
FooGraphqlQuery.kt: 'operation' hides member of supertype 'GraphQLQuery' and needs 'override' modifier
This includes the root
and parent
names used by the projection base classes.
Currently the Kotlin generator only ignores fields named _
.
Possibly use the Kotlinpoet NameAllocator
.
Having 2 queries like:
season(id: ID!): Season
season(year: Int!): Season
this will generate the following DgsConstants
code:
public static class QUERY {
public static final String Season = "season";
public static final String Season = "season";
..
}
which contains a duplicate constant, causing a compilation failure unfortunately.
By just applying the plugin codegen should work in a basic configuration, so the "generateJava" configuration should be optional.
The plugin in it's default configuration adds the directory build/generated
to the main sourceSet.
However, this is problematic, since this directory is already used as the output directory for other methods of code generation, like annotation processors.
I ran into this problem in a project where an annotation processor is used for the test sourceSet for which the generated code is placed in build/generated/sources/annotationProcessor.java/test
.
Since these generated classes have dependencies which are only available in the test classpath, adding build/generated
to the main sourceSet breaks the build process.
I'm able to work around this by changing the output directory for this plugin and manipulating the main sourceSet like so:
tasks.withType(com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask) {
generatedSourcesDir = "${project.buildDir.absolutePath}/dgs-codegen"
}
sourceSets {
main {
java {
def dirs = srcDirs
dirs.remove(file("${project.buildDir.absolutePath}/generated"))
srcDirs = dirs
srcDir "${project.buildDir.absolutePath}/dgs-codegen/generated"
}
}
}
But I believe that it would be better if this plugin would put its generated sources in some sub directory of build/generated
instead, where they don't interfere with other generated sources.
It would be great if there was a Companion object created for each of the generated data classes. This would let developers attach static extension methods to the data classes. In my particular use case I want to attach a fromProtobuf()
method to the Foo
type defined in my schema so I can easily marshal from one format to the other with the sytnax return Foo.fromProtobuf(fooProto)
in my data fetcher.
Scalar type code generation failed with Class not found error
error: class QUERY is already defined in class DgsConstants
error: class MUTATION is already defined in class DgsConstants
The problem is I had 2 schema files for code gen and dgs codegen generated common static classes for both the schema files.
Can we have a way where along with list of files, I can specify the generated package? So that each graphql file gets its own package.
When generateClient is set true in v4.4.0 I am encountering the following stack trace (trimmed for relevancy). Doesn't occur in 4.2.0
Caused by: java.lang.NoSuchMethodError: kotlin.sequences.SequencesKt.flatMapIterable(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
at com.netflix.graphql.dgs.codegen.CodeGenKt.filterInterfaceFields(CodeGen.kt:426)
at com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinClientApiGenerator.createSubProjectionType(ClientApiGenerator.kt:346)
at com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinClientApiGenerator.createSubProjection(ClientApiGenerator.kt:319)
at com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinClientApiGenerator.createRootProjection(ClientApiGenerator.kt:172)
at com.netflix.graphql.dgs.codegen.generators.kotlin.KotlinClientApiGenerator.generate(ClientApiGenerator.kt:39)
at com.netflix.graphql.dgs.codegen.CodeGen$generateKotlinClientApi$2.invoke(CodeGen.kt:247)
at com.netflix.graphql.dgs.codegen.CodeGen$generateKotlinClientApi$2.invoke(CodeGen.kt:31)
at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:172)
at com.netflix.graphql.dgs.codegen.CodeGen.generateKotlinClientApi(CodeGen.kt:544)
at com.netflix.graphql.dgs.codegen.CodeGen.generateKotlinForSchema(CodeGen.kt:235)
at com.netflix.graphql.dgs.codegen.CodeGen.generate(CodeGen.kt:69)
at com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask.generate(GenerateJavaTask.kt:139)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
E.g with.
type Query {
shows(titleFilter: String): [Show]
}
an actual datafetcher implementation could look like:
@DgsComponent
public class ShowsDatafetcher {
private final ShowsService showsService;
public ShowsDatafetcher(ShowsService showsService) {
this.showsService = showsService;
}
/**
* This datafetcher resolves the shows field on Query.
* It uses an @InputArgument to get the titleFilter from the Query if one is defined.
*/
@DgsData(parentType = DgsConstants.QUERY_TYPE, field = DgsConstants.QUERY.Shows)
public List<Show> shows(@InputArgument("titleFilter") String titleFilter) {
if (titleFilter == null) {
return showsService.shows();
}
return showsService.shows().stream().filter(s -> s.getTitle().contains(titleFilter)).collect(Collectors.toList());
}
}
With the generated DgsConstants
we can nicely do:
@DgsData(parentType = DgsConstants.QUERY_TYPE, field = DgsConstants.QUERY.Shows)
but the input filter still has a coded String, which could cause some issue when changing the name later in the scheme.
@InputArgument("titleFilter")
It would be nice to be able do something like:
@InputArgument(DgsConstants.SHOWS.TitleFilter)
The generated DGS constants are uppercased and they are a bit hard to parse, especially for reviewers.
For example, the type DgsArtifactVersionInEnvironment
will be converted into DgsConstants.DGSARTIFACTVERSIONINENVIRONMENT.TYPE_NAME
.
One way to solve that without breaking existing apps is to add an option to the plugin config to set a delimiter (e.g. _
).
A schema such as
direction: SortDirection! = ASC
results in code that doesn't compile:
private SortDirection direction = EnumValue{name='ASC'};
If a type is defined in one file and extended in others, it is possible for invalid subprojections to be created. Please see the associated pull request for details and a test case demonstrating the issue.
Add generator switch for using boxed types for non-nullable primitives. As discussed here Netflix/dgs-framework#33 (reply in thread)
Having a schema like:
type Query {
races: [Race!]!
race(id: ID!): Race
circuits: [Circuit!]!
circuit(id: ID!): Circuit
}
type Race {
id: Int!
year: Int!
rount: Int!
circuit: Circuit!
circuitType: CircuitType!
}
type Circuit {
id: Int
name: String
type: CircuitType!
}
enum CircuitType {
RACE
ROAD
}
compiling the generated DGS GraphQL client gives the following compilation error:
incompatible types: RaceProjectionRoot cannot be converted to RaceCircuitProjection
RaceCircuitTypeProjection projection = new RaceCircuitTypeProjection(this, this);
^
Maybe special about the schema is:
Circuit.type
--> CircuitType
Race.circuit
--> Circuit
Race.circuitType
--> CircuitType
I think the generator gets confused as names are similar / concatenated?
A sample github project showcasing this issue is here.
Hi. Please let me open the issue.
I tried to generate code from GraphQL schema.
However, in some cases, the generated projection method is missing.
I use plugin - v4.2.0
.
The below is the GraphQL schema and the generated code (projection) .
The generated code is code that is missing a method.
schema.graphql
type Query {
shows(titleFilter: String!): [Show]
}
type Show {
title: String!
list1: [List1!]
list2: [List2!]
}
interface List1 {
id: String!
}
type List1A implements List1 {
id: String!
name: String!
option: Option!
}
type List1B implements List1 {
id: String!
name: String!
option: Option!
}
type List1C implements List1 {
id: String!
name: String!
option: Option!
}
interface List2 {
id: String!
}
type List2A implements List2 {
id: String!
name: String!
option: Option!
}
type List2B implements List2 {
id: String!
name: String!
option: Option!
}
type List2C implements List2 {
id: String!
name: String!
option: Option!
}
type Option {
name: String!
memo: String!
}
ShowsList2List2BProjection.kt
package com.example.dgs.demo.generated.client
import com.netflix.graphql.dgs.client.codegen.BaseSubProjectionNode
import kotlin.String
public class ShowsList2List2BProjection(
parent: ShowsList2Projection,
root: ShowsProjectionRoot
) : BaseSubProjectionNode<ShowsList2Projection, ShowsProjectionRoot>(parent, root) {
init {
fields["__typename"] = null
}
public fun name(): ShowsList2List2BProjection {
fields["name"] = null
return this
}
public override fun toString(): String {
val builder = StringBuilder()
builder.append("... on List2B {")
fields.forEach { k, v ->
builder.append(" ").append(k)
if(v != null) {
builder.append(" ").append(v.toString())
}
}
builder.append("}")
return builder.toString()
}
}
I think the following method should also be included.
public fun option(): ShowsList2List2BOptionProjection {
val projection = ShowsList2List2BOptionProjection(this, root)
fields["option"] = projection
return projection
}
I don't know the cause for sure, but if I reduce the number of types that implement interface in GraphQL schema, this problem doesn't seem to occur.
I noticed that upgrading the codeGen to 4.4.2
version causes the following bug
Codegen config: --output-dir=/Users/sergiobilello/Documents/repositories/test-settings-graphql/build/generated
--package-name=us.test.graphql.settings.graph.schema
--sub-package-name-client=client
--sub-package-name-datafetchers=datafetchers
--sub-package-name-types=types
--write-to-disk
--language=JAVA
--generate-client
--generate-data-types
--type-mapping JSON=us.test.graphql.settings.graph.scalar.JsonScalar
Generating InlineResponse200GraphQLQuery
Generating InlineResponse200ProjectionRoot
:generateJava (Thread[Execution worker for ':',5,main]) completed. Took 1.427 secs.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':generateJava'.
> kotlin.sequences.SequencesKt.flatMapIterable(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
At Netflix we ship common types as libraries, with schemas in META-INF/schema
.
Currently we can't add these schemas to the code generation configuration, so these types aren't known during generation. This requires a workaround with typeMappings
.
Ideally we should also support a mechanism to provide a typeMapping
in the JAR file, e.g. with a mapping file in META-INF
, so that we can extend the typeMappings based on libraries.
First of all, I really like your new GraphQL framework and this code generation plugin! It makes lots of stuff easier. I tried a few things and ran into a limitation using this code generator.
For example a schema looks like this:
interface Animal {
name: String
animalType: AnimalType
}
type Dog implements Animal {
name: String
animalType: AnimalType
color: String
}
type Cat implements Animal {
name: String
animalType: AnimalType!
eat: String
}
enum AnimalType {
DOG
CAT
}
The resulting interface for Animal has the following JsonTypeInfo:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "__typename"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat")
})
The problem with this is, that I would like use a custom @JsonSubTypes configuration, using an existing property mapped to an enum value. It should look something like this:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "animalType",
visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "DOG"),
@JsonSubTypes.Type(value = Cat.class, name = "CAT")
})
I realize that this is probably a complex issue, but I would like to know if something like this is even possible or a too specific problem?
With this the generator could be used to create a GraphQL schema for an existing service or database and create an lightweight GraphQL service ontop.
For the following input type,
input AuthorInput {
name: String!
}
dgs-codegen generates the following kotlin data class
.
public data class AuthorInput(
@JsonProperty("name")
public val name: String
) {
public override fun toString(): String = linkedMapOf(
"name" to ("\"" + name + "\""),
).map { it.key + ":" + it.value }.joinToString(",", "{", "}")
public companion object
}
This code compiles in Kotlin 1.4 or greater but doesn't compile less than 1.4 due to the trailing comma. I think the solution would be removing the unnecessary trailing comma regardless of kotlin versions or removing toString()
method as data class
itself overrides toString()
.
I update the Federation examples with new release version v4.4.3 and see the following error:
> Task :generateJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':generateJava'.
> kotlin.sequences.SequencesKt.flatMapIterable(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':generateJava'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:208)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:263)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:206)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:187)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:372)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:359)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:352)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:338)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: java.lang.NoSuchMethodError: kotlin.sequences.SequencesKt.flatMapIterable(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
at com.netflix.graphql.dgs.codegen.CodeGenKt.filterInterfaceFields(CodeGen.kt:427)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createSubProjectionType(ClientApiGenerator.kt:351)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createFragment(ClientApiGenerator.kt:290)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createEntitiesRootProjection(ClientApiGenerator.kt:236)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.generateEntities(ClientApiGenerator.kt:53)
at com.netflix.graphql.dgs.codegen.CodeGen.generateJavaClientEntitiesApi(CodeGen.kt:148)
at com.netflix.graphql.dgs.codegen.CodeGen.generateForSchema(CodeGen.kt:95)
at com.netflix.graphql.dgs.codegen.CodeGen.generate(CodeGen.kt:49)
at com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask.generate(GenerateJavaTask.kt:143)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.run(ExecuteActionsTaskExecuter.java:570)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:395)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:387)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:84)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:555)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:538)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:109)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:279)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:268)
at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$1(ExecuteStep.java:33)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:33)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:67)
at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:36)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:49)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:34)
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:43)
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73)
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54)
at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:34)
at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:44)
at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:54)
at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:38)
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49)
at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:159)
at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:72)
at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:43)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:44)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:33)
at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38)
at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24)
at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:92)
at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:85)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:55)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:39)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26)
at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:94)
at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:79)
at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:53)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:74)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:78)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:78)
at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:39)
at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:40)
at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:28)
at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:195)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:187)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:372)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:359)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:352)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:338)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
* Get more help at https://help.gradle.org
BUILD FAILED in 1s
1 actionable task: 1 executed
I am running the codegen 4.4.1 and getting a stack trace:
I latest one I could find that works is 4.2.0
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: java.lang.NoSuchMethodError: kotlin.sequences.SequencesKt.flatMapIterable(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
at com.netflix.graphql.dgs.codegen.CodeGenKt.filterInterfaceFields(CodeGen.kt:426)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createSubProjectionType(ClientApiGenerator.kt:351)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createFragment(ClientApiGenerator.kt:290)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.addFragmentProjectionMethod(ClientApiGenerator.kt:286)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createUnionTypes(ClientApiGenerator.kt:261)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createSubProjectionType(ClientApiGenerator.kt:397)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createSubProjection(ClientApiGenerator.kt:329)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.createRootProjection(ClientApiGenerator.kt:184)
at com.netflix.graphql.dgs.codegen.generators.java.ClientApiGenerator.generate(ClientApiGenerator.kt:38)
at com.netflix.graphql.dgs.codegen.CodeGen$generateJavaClientApi$2.invoke(CodeGen.kt:137)
at com.netflix.graphql.dgs.codegen.CodeGen$generateJavaClientApi$2.invoke(CodeGen.kt:31)
at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:172)
at com.netflix.graphql.dgs.codegen.CodeGen.generateJavaClientApi(CodeGen.kt:498)
I see 4.2.1
release was created 17 hours ago - and also the tag exists - but it seems not to be available on the Gradle Plugin Portal.
This is to avoid keyword clashes for fields that have the same name as kotlin/java keyworkds, e.g void, default etc.
for the following schema.graphqls:
type Query {
test(input1: String): String
}
type Mutation {
test(input2: Int): Boolean
}
the object is generated only for the mutation:
public class TestGraphQLQuery extends GraphQLQuery {
public TestGraphQLQuery(Integer input2) {
super("mutation");
if (input2 != null) {
getInput().put("input2", input2);
}
}
public TestGraphQLQuery() {
super("mutation");
}
@Override
public String getOperationName() {
return "test";
}
public static Builder newRequest() {
return new Builder();
}
public static class Builder {
private Integer input2;
public TestGraphQLQuery build() {
return new TestGraphQLQuery(input2);
}
public Builder input2(Integer input2) {
this.input2 = input2;
return this;
}
}
}
and for the schema.graphqls:
type Subscription {
test(input3: ID): Float
}
there is no request object generated, just a Type one for Subscription
public class Subscription {
private Double test;
public Subscription() {
}
public Subscription(Double test) {
this.test = test;
}
public Double getTest() {
return test;
}
public void setTest(Double test) {
this.test = test;
}
@Override
public String toString() {
return "Subscription{" + "test='" + test + "'" +"}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Subscription that = (Subscription) o;
return java.util.Objects.equals(test, that.test);
}
@Override
public int hashCode() {
return java.util.Objects.hash(test);
}
public static gcld.lib.demo.model.types.Subscription.Builder newBuilder() {
return new Builder();
}
public static class Builder {
private Double test;
public Subscription build() {
gcld.lib.demo.model.types.Subscription result = new gcld.lib.demo.model.types.Subscription();
result.test = this.test;
return result;
}
public gcld.lib.demo.model.types.Subscription.Builder test(Double test) {
this.test = test;
return this;
}
}
}
similarly, for schema.graphqls
type Query {
test(input1: String): String
test(input2: String, input3: String): String
}
the GraphQLGuery created will only support the second query, not the first
I would suggest creating separate generated objects for each mutation / query, as well as adding support for subscriptions.
The current calculation counts the number of sub projections created in total for a root query instead of the depth.
Hello,
I switched to latest version of codegen plugin (4.3.4) and the client generated is not compilable due to this error. I think the null check should not happen for type int
code generated
public IntAsInputGraphQLQuery(int number) {
super("mutation");
if (number != null) {
getInput().put("number", number);
}
}
dgs-examples-java/build/generated/com/example/demo/generated/client/IntAsInputGraphQLQuery.java:10: error: bad operand types for binary operator '!='
if (number != null) {
^
first type: int
second type: <null>
FYI codegen plugin version 4.0.12 works fine and the check for null is not there for int types
I replicated the issue in my fork https://github.com/Fruko/dgs-examples-java
see ScalarDataFetcher.java
method intAsInput
type Query {
shows(titleFilter: String): [Show]
}
When creating a request without providing a titleFilter, serializing creates an invalid query {shows(){ title } }
.
ShowsGraphQLQuery.newRequest().build()
While passing in an empty filter works:
ShowsGraphQLQuery.newRequest().titleFilter("").build()
Code generation fails to detect Kotlin language if the Kotlin plugin is defined after the dgs.codegen plugin.
Doesn't work:
plugins {
id("com.netflix.dgs.codegen") version "4.4.3"
kotlin("jvm") version "1.4.31"
}
Works:
plugins {
kotlin("jvm") version "1.4.31"
id("com.netflix.dgs.codegen") version "4.4.3"
}
If an Input object has double quotes in a String field, they are not escaped when the GraphQL input parameter is created with toString(). This breaks the serialized GraphQLQueryRequest.
Having the following Query
in the schema:
type Query {
shows(titleFilter: String): [Show]
}
DGS codegen generates the example datafetcher like:
@DgsComponent
public class ShowsDatafetcher {
@DgsData(
parentType = "Query",
field = "shows"
)
public List<Show> getShows(DataFetchingEnvironment dataFetchingEnvironment) {
return null;
}
}
DgsConstants
for the @DgsData
parentType
and field
value? This way when copying the example datafetcher to actual project code would not need manual adaption anymore.@InputArgument("titleFilter") String titleFilter
argument instead of DataFetchingEnvironment dataFetchingEnvironment
?E.g. it could generate:
@DgsComponent
public class ShowsDatafetcher {
@DgsData(parentType = DgsConstants.QUERY_TYPE, field = DgsConstants.QUERY.Shows)
public List<Show> shows(@InputArgument("titleFilter") String titleFilter) {
return null;
}
}
This is to support implementing an interface by allowing an implementing field to return a subtype of the interface field's return type.
interface Friendly {
bestFriend: Friendly
}
type Person implements Friendly {
bestFriend: Person
}
// Generated java interface
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "__typename"
)
@JsonSubTypes(@JsonSubTypes.Type(value = Person.class, name = "Person"))
public interface Friendly {
Friendly getBestFriend();
void setBestFriend(Friendly bestFriend);
}
// generated java class
public class Person implements Friendly {
private Person bestFriend;
public Person getBestFriend() {
return bestFriend;
}
public void setBestFriend(Person bestFriend) {
this.bestFriend = bestFriend;
}
The generated java code doesn't compile, as the class needs to implement the setter with "interface" parameter definition like that:
public void setBestFriend(Friendly bestFriend) {
this.bestFriend = bestFriend;
}
Inspecting the generated code, looks like there is no ability to supply arguments to fields in projections nor is there ability to supply aliases either.
https://graphql.org/learn/queries/#arguments
https://graphql.org/learn/queries/#aliasesThis behavior can be reproduced by tweaking the example from the Getting started guide:
type Votes { starRating(min: Float): Float nrOfVotes: Int }
After generating for this example, there is no way to setmin
on the fieldstarRating
via theTicksEdgesNodeRouteVotesProjection.java
methods.
Re-creating Netflix/dgs-framework#63 (comment) (original issue description above) as this is a dgs-codegen issue.
I have tried to generate code for an interface with two extending types based on your demo Show
type.
The ShowsProjectionRoot
for the interface is generated with an unresolved reference root
in the on...()
methods, which is used for the extending types.
Here is one of the on...()
methods with unresolved reference root
:
public fun onComedyShow(): ShowsComedyShowProjection {
val fragment = ShowsComedyShowProjection(this, root)
fragments.add(fragment)
return fragment;
}
Here is the schema definition I have used:
interface Show {
id: Int
title: String
releaseYear: Int
director: String
reviews: [Review]
artwork: [Image]
}
type HorrorShow implements Show {
id: Int
title: String
releaseYear: Int
director: String
reviews: [Review]
artwork: [Image]
screamsCount: Int
}
type ComedyShow implements Show {
id: Int
title: String
releaseYear: Int
director: String
reviews: [Review]
artwork: [Image]
laughsCount: Int
}
As discussed here I would personally like it if the codegen tool could generate interfaces for the types (complementary not instead of).
Currently a schema like
input ColorFilter {
color: String = "red"
}
generates a Kotlin class like (shortened to the constructor)
public data class ColorFilter(
@JsonProperty("color")
public val color: String? = "red"
)
According to the spec
An input field is required if it has a non‐null type and does not have a default value. Otherwise, the input object field is optional.
To my understanding this would allow to generate
public data class ColorFilter(
@JsonProperty("color")
public val color: String = "red"
)
as the field will be always set in this case. This would using the generated class a tad easier as it avoids using ?.
or !!
.
Lines
val srcDirs = mainSourceSet.java.srcDirs + outputDir
mainSourceSet.java.setSrcDirs(srcDirs)
wipe out Gradle task dependency data entirely and break all of build dependency logic.
Instead it should be:
mainSourceSet.java.srcDirs(outputDir)
interface SCHEDPhaseSegmentTag {
}
interface SCHEDPhasePauseTag implements SCHEDPhaseSegmentTag {
}
Caused by: graphql.parser.InvalidSyntaxException: Invalid Syntax : There are more tokens in the query that have not been consumed offending token 'implements' at line 2386 column 30
at graphql.parser.ExtendedBailStrategy.mkMoreTokensException(ExtendedBailStrategy.java:42)
at graphql.parser.Parser.parseDocument(Parser.java:93)
at graphql.parser.Parser.parseDocument(Parser.java:39)
at graphql.parser.Parser.parseDocument(Parser.java:31)
at com.netflix.graphql.dgs.codegen.CodeGen.generateForSchema(CodeGen.kt:57)
at com.netflix.graphql.dgs.codegen.CodeGen.generate(CodeGen.kt:27)
at com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask.generate(GenerateJavaTask.kt:77)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
i tried to setup the codegen plugin in a gradle multimodule setup. This fails:
Using gradle at '/Users/pho/Projects/DgsCodegen/gradlew' to run buildfile '/Users/pho/Projects/DgsCodegen/build.gradle.kts':
Starting a Gradle Daemon, 1 stopped Daemon could not be reused, use --status for details
> Task :schema:generateJava FAILED
ANTLR Tool version 4.8 used for code generation does not match the current runtime version 4.5.2ANTLR Runtime version 4.8 used for parser compilation does not match the current runtime version 4.5.2
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':schema:generateJava'.
> java.lang.ExceptionInInitializerError (no error message)
* 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
I created a stripped down setup that can be used to reproduce the issue:
https://github.com/abendt/DgsCodeGen
The issue only seems to occur when the Kotlin plugin is defined in the top level module. If you make the following changes you can build the project:
The downside of this workaround is that we cannot have certain configurations for all modules in the top level (e.g. kotlin compiler settings) as this requires to have the plugin defined there.
I'm using common gradle submodule to have common types that I extend in different apps
But the problem is that it doesn't add extended enum values.
Looks like a bug.
common/src/main/resources/graphql/file1.graphql
enum MyEnum {
A
B
}
app1/src/main/resources/graphql/file2.graphql
extend enum MyEnum {
C
}
Plugin config:
schemaPaths = listOf("$rootDir/apps/common/src/main/resources/graphql", "${projectDir}/src/main/resources/graphql").toMutableList()
Generated class:
public enum class WalletType {
A,
B,
}
The ClientApiGenerator
is logging a lot of Generating foobar
messages.
E.g. println("Generating ${it.name.capitalize()}GraphQLQuery")
This is quite verbose and it is printed everytime a project is run...
I think it would be good to remove it or reduce the logging level...
Hi all, first off thanks so much for open-sourcing this project! As we were evaluating this internally we ran into an issue for extended Entities in the generated code for client projections. The property added to the extended Entity is not present in the generated code.
type JavaBook @key(fields: "id") {
"The unique identifier of the javaBook"
id: ID!
"The title of the javaBook"
title: String
"The Synopsis of the javaBook"
description: String
"The publication year of the javaBook"
yearPublished: Int
"Genre of the javaBook"
genre: JavaGenre
"Author of the javaBook"
author: JavaAuthor
"Similar books based on the Genre"
similarBooks: [JavaBook]
}
type JavaAuthor @key(fields: "id") @extends {
"The unique identifier of the author"
id: ID! @external
books: [JavaBook]
}
enum JavaGenre {
FICTION,
NON_FICTION
}
Generates the following:
package com.acme.dgs.generated.client;
import com.netflix.graphql.dgs.client.codegen.BaseSubProjectionNode;
import java.lang.Override;
import java.lang.String;
public class EntitiesJavaAuthorKeyProjection extends BaseSubProjectionNode<EntitiesProjectionRoot, EntitiesProjectionRoot> {
{
getFields().put("__typename", null);
}
public EntitiesJavaAuthorKeyProjection(EntitiesProjectionRoot parent,
EntitiesProjectionRoot root) {
super(parent, root);
}
public EntitiesJavaAuthorKeyProjection id() {
getFields().put("id", null);
return this;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("... on JavaAuthor {");
getFields().forEach((k, v) -> {
builder.append(" ").append(k);
if(v != null) {
builder.append(" ").append(v.toString());
}
});
builder.append("}");
return builder.toString();
}
}
We had to add the following:
public EntitiesJavaBookKeyAuthorBooksProjection books() {
EntitiesJavaBookKeyAuthorBooksProjection projection =
new EntitiesJavaBookKeyAuthorBooksProjection(null, getRoot());
getFields().put("books", projection);
return projection;
}
define in scheme like:
{
enum Duration {
DAY
NIGHT
WEEK
}
}
generated java like:
public enum Duration {
DAY, NIGHT, WEEK;
private Duration() { /* compiled code */ }
}
Do we support to generated java enum with values like:
public enum Duration {
DAY("day"), NIGHT("night"), WEEK("week");
private String value;
private Duration(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
If my graphql schema defines a type that looks like this:
type ProductType {
....
....
productClasses: [ProductClassType]
masterClass: ProductClassType
....
}
The code and query generator will create a projection root that contains only one of the fields (the first one from the top). Thus in the example above, the masterClass
will not be in the generated projection code.
The environment:
As discussed here Netflix/dgs-framework#20 it would be useful to set a prefix/suffix for generated classes.
E.g. when having a Show
class in a persistent layer already, generating another GraphQL Show
type class would mean that FQN need to be used when mapping the two objects.
For users interested this could be solved by e.g. prefixing all generated classes with Graphql
(resulting in GraphqlShow
) or suffixing it with e.g. DTO
or Type
(resulting in resp. ShowDTO
or ShowType
).
While typing this I'm also thinking about the generated classes for the enums in the GraphQL schema.
Maybe a different suffix would be desired here like Enum
(resulting in e.g. CategoryEnum
having a Category
enum in the schema).
So maybe good to be able to configure the prefix/suffix per generated type?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.