diff --git a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/FederationDotNames.java b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/FederationDotNames.java new file mode 100644 index 000000000..4d7083c04 --- /dev/null +++ b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/FederationDotNames.java @@ -0,0 +1,44 @@ +package io.smallrye.graphql.gradle.tasks; + +import io.smallrye.graphql.api.federation.Authenticated; +import io.smallrye.graphql.api.federation.ComposeDirective; +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.Inaccessible; +import io.smallrye.graphql.api.federation.InterfaceObject; +import io.smallrye.graphql.api.federation.Key; +import io.smallrye.graphql.api.federation.Override; +import io.smallrye.graphql.api.federation.Provides; +import io.smallrye.graphql.api.federation.Requires; +import io.smallrye.graphql.api.federation.Shareable; +import io.smallrye.graphql.api.federation.Tag; +import io.smallrye.graphql.api.federation.link.Link; +import io.smallrye.graphql.api.federation.policy.Policy; +import io.smallrye.graphql.api.federation.requiresscopes.RequiresScopes; +import org.jboss.jandex.DotName; + +import java.util.ArrayList; +import java.util.List; + +public final class FederationDotNames { + public static final List FEDERATION_DIRECTIVES_NAMES; + + static { + FEDERATION_DIRECTIVES_NAMES = new ArrayList<>(); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Authenticated.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(ComposeDirective.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Extends.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(External.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Inaccessible.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(InterfaceObject.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Key.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Override.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Provides.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Requires.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Shareable.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Tag.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Link.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(Policy.class)); + FEDERATION_DIRECTIVES_NAMES.add(DotName.createSimple(RequiresScopes.class)); + } +} diff --git a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GenerateSchemaTask.java b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GenerateSchemaTask.java index fbe319d9f..e4a0c44e8 100644 --- a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GenerateSchemaTask.java +++ b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GenerateSchemaTask.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.lang.annotation.Repeatable; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -13,6 +14,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -36,11 +38,37 @@ import org.jboss.jandex.Result; import graphql.schema.GraphQLSchema; +import io.smallrye.graphql.api.Deprecated; +import io.smallrye.graphql.api.Entry; +import io.smallrye.graphql.api.OneOf; +import io.smallrye.graphql.api.federation.Authenticated; +import io.smallrye.graphql.api.federation.ComposeDirective; +import io.smallrye.graphql.api.federation.Extends; +import io.smallrye.graphql.api.federation.External; +import io.smallrye.graphql.api.federation.FieldSet; +import io.smallrye.graphql.api.federation.Inaccessible; +import io.smallrye.graphql.api.federation.InterfaceObject; +import io.smallrye.graphql.api.federation.Key; +import io.smallrye.graphql.api.federation.Override; +import io.smallrye.graphql.api.federation.Provides; +import io.smallrye.graphql.api.federation.Requires; +import io.smallrye.graphql.api.federation.Shareable; +import io.smallrye.graphql.api.federation.Tag; +import io.smallrye.graphql.api.federation.link.Import; +import io.smallrye.graphql.api.federation.link.Link; +import io.smallrye.graphql.api.federation.link.Purpose; +import io.smallrye.graphql.api.federation.policy.Policy; +import io.smallrye.graphql.api.federation.policy.PolicyGroup; +import io.smallrye.graphql.api.federation.policy.PolicyItem; +import io.smallrye.graphql.api.federation.requiresscopes.RequiresScopes; +import io.smallrye.graphql.api.federation.requiresscopes.ScopeGroup; +import io.smallrye.graphql.api.federation.requiresscopes.ScopeItem; import io.smallrye.graphql.bootstrap.Bootstrap; import io.smallrye.graphql.execution.SchemaPrinter; import io.smallrye.graphql.schema.SchemaBuilder; import io.smallrye.graphql.schema.model.Schema; -import io.smallrye.graphql.spi.config.Config; + +import static io.smallrye.graphql.gradle.tasks.FederationDotNames.FEDERATION_DIRECTIVES_NAMES; /** * Generate schema task. @@ -178,10 +206,17 @@ public static GradleConfig getConfig() { @TaskAction public void generateSchema() throws Exception { - this.config = new GradleConfig(includeScalars, includeDirectives, includeSchemaDefinition, includeIntrospectionTypes); + config = new GradleConfig(includeScalars, includeDirectives, includeSchemaDefinition, includeIntrospectionTypes); ClassLoader classLoader = getClassLoader(); Thread.currentThread().setContextClassLoader(classLoader); IndexView index = createIndex(); + + if (hasFederationDirectives(index)) { + config.setFederationEnabled(true); + System.setProperty("smallrye.graphql.federation.enabled", "true"); + index = CompositeIndex.create(index, createFederationApiIndex()); + } + String schema = generateSchema(index); if (schema != null) { write(schema); @@ -190,6 +225,13 @@ public void generateSchema() throws Exception { } } + private static boolean hasFederationDirectives(IndexView index) { + return index.getKnownClasses().stream() + .anyMatch(classInfo -> FEDERATION_DIRECTIVES_NAMES.stream() + .anyMatch(classInfo::hasAnnotation) + ); + } + private IndexView createIndex() { IndexView moduleIndex; try { @@ -227,6 +269,45 @@ private IndexView createIndex() { } } + private IndexView createFederationApiIndex() throws IOException { + Indexer indexer = new Indexer(); + indexer.indexClass(Map.class); + indexer.indexClass(Entry.class); + indexer.indexClass(Repeatable.class); + + indexer.indexClass(Deprecated.class); + indexer.indexClass(OneOf.class); + + // directives from the API module + indexer.indexClass(Authenticated.class); + indexer.indexClass(ComposeDirective.class); + indexer.indexClass(Extends.class); + indexer.indexClass(External.class); + indexer.indexClass(FieldSet.class); + indexer.indexClass(Inaccessible.class); + indexer.indexClass(InterfaceObject.class); + indexer.indexClass(Key.class); + indexer.indexClass(Override.class); + indexer.indexClass(Provides.class); + indexer.indexClass(Requires.class); + indexer.indexClass(Shareable.class); + indexer.indexClass(Tag.class); + + indexer.indexClass(Link.class); + indexer.indexClass(Import.class); + indexer.indexClass(Purpose.class); + + indexer.indexClass(Policy.class); + indexer.indexClass(PolicyGroup.class); + indexer.indexClass(PolicyItem.class); + + indexer.indexClass(RequiresScopes.class); + indexer.indexClass(ScopeGroup.class); + indexer.indexClass(ScopeItem.class); + + return indexer.complete(); + } + // index the classes of this Gradle module private Index indexModuleClasses() throws IOException { Indexer indexer = new Indexer(); diff --git a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfig.java b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfig.java index d07d19b4a..900e88b93 100644 --- a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfig.java +++ b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfig.java @@ -10,7 +10,10 @@ public class GradleConfig { private final boolean includeIntrospectionTypes; - public GradleConfig(boolean includeScalars, boolean includeDirectives, boolean includeSchemaDefinition, boolean includeIntrospectionTypes) { + private boolean federationEnabled; + + public GradleConfig(boolean includeScalars, boolean includeDirectives, boolean includeSchemaDefinition, + boolean includeIntrospectionTypes) { this.includeScalars = includeScalars; this.includeDirectives = includeDirectives; this.includeSchemaDefinition = includeSchemaDefinition; @@ -32,4 +35,12 @@ public boolean isIncludeSchemaDefinition() { public boolean isIncludeIntrospectionTypes() { return includeIntrospectionTypes; } + + public void setFederationEnabled(boolean federationEnabled) { + this.federationEnabled = federationEnabled; + } + + public boolean isFederationEnabled() { + return federationEnabled; + } } diff --git a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfigFacade.java b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfigFacade.java index 198078303..7ca51a7f3 100644 --- a/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfigFacade.java +++ b/tools/gradle-plugin/plugin/src/main/java/io/smallrye/graphql/gradle/tasks/GradleConfigFacade.java @@ -12,6 +12,8 @@ public class GradleConfigFacade implements Config { private boolean includeIntrospectionTypes; + private boolean federationEnabled; + // Constructor used by the ServiceLoader mechanism. When this is called, we assume that GenerateSchemaTask // has already produced a config based on the environment, and we just make a copy of it public GradleConfigFacade() { @@ -20,17 +22,19 @@ public GradleConfigFacade() { this.includeDirectives = instance.isIncludeDirectives(); this.includeSchemaDefinition = instance.isIncludeSchemaDefinition(); this.includeIntrospectionTypes = instance.isIncludeIntrospectionTypes(); - + this.federationEnabled = instance.isFederationEnabled(); } public GradleConfigFacade(boolean includeScalarsInSchema, boolean includeDirectivesInSchema, boolean includeSchemaDefinitionInSchema, - boolean includeIntrospectionTypesInSchema) { + boolean includeIntrospectionTypesInSchema, + boolean federationEnabled) { this.includeScalars = includeScalarsInSchema; this.includeDirectives = includeDirectivesInSchema; this.includeSchemaDefinition = includeSchemaDefinitionInSchema; this.includeIntrospectionTypes = includeIntrospectionTypesInSchema; + this.federationEnabled = federationEnabled; } @Override @@ -57,4 +61,9 @@ public boolean isIncludeSchemaDefinitionInSchema() { public boolean isIncludeIntrospectionTypesInSchema() { return includeIntrospectionTypes; } + + @Override + public boolean isFederationEnabled() { + return federationEnabled; + } } diff --git a/tools/gradle-plugin/src/test/java/io/smallrye/graphql/gradle/test/GradlePluginGenerateSchemaIncludeDirectivesTest.java b/tools/gradle-plugin/src/test/java/io/smallrye/graphql/gradle/test/GradlePluginGenerateSchemaIncludeDirectivesTest.java index a03f0ff2f..c985a2b80 100644 --- a/tools/gradle-plugin/src/test/java/io/smallrye/graphql/gradle/test/GradlePluginGenerateSchemaIncludeDirectivesTest.java +++ b/tools/gradle-plugin/src/test/java/io/smallrye/graphql/gradle/test/GradlePluginGenerateSchemaIncludeDirectivesTest.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.gradle.testkit.runner.BuildResult; @@ -30,8 +29,13 @@ public void cleanup() { @Test public void testIncludeDirectives() throws IOException { - String schema = execute(Collections.singletonList("-DincludeDirectives=true")); + String schema = execute(List.of( + "-DincludeDirectives=true", + "-DincludeScalars=true", + "-DincludeSchemaDefinition=true")); assertTrue(schema.contains("directive @skip"), "Directives should be included: " + schema); + assertTrue(schema.contains("_entities(representations")); + assertTrue(schema.contains("type Foo @key(fields : \"id\")")); } private String execute(List arguments) throws IOException { diff --git a/tools/gradle-plugin/testing-project-kotlin/build.gradle b/tools/gradle-plugin/testing-project-kotlin/build.gradle index 409776bcf..3f7f8495a 100644 --- a/tools/gradle-plugin/testing-project-kotlin/build.gradle +++ b/tools/gradle-plugin/testing-project-kotlin/build.gradle @@ -5,7 +5,7 @@ plugins { } dependencies { - implementation "io.smallrye:smallrye-graphql:1.3.2" + implementation "io.smallrye:smallrye-graphql:2.10.0" } repositories { diff --git a/tools/gradle-plugin/testing-project/build.gradle b/tools/gradle-plugin/testing-project/build.gradle index 2ca9743fa..b7c0a035b 100644 --- a/tools/gradle-plugin/testing-project/build.gradle +++ b/tools/gradle-plugin/testing-project/build.gradle @@ -4,7 +4,7 @@ plugins { } dependencies { - implementation "io.smallrye:smallrye-graphql:1.3.2" + implementation "io.smallrye:smallrye-graphql:2.10.0" } repositories { @@ -14,7 +14,9 @@ repositories { generateSchema { // FIXME: how to properly pass properties to runs? see the comment in GradlePluginGenerateSchemaTest.java + includeScalars = Boolean.getBoolean("includeScalars") includeDirectives = Boolean.getBoolean("includeDirectives") + includeSchemaDefinition = Boolean.getBoolean("includeSchemaDefinition") } group 'org.acme' diff --git a/tools/gradle-plugin/testing-project/src/main/java/org/acme/Foo.java b/tools/gradle-plugin/testing-project/src/main/java/org/acme/Foo.java index 0a8006442..adb99d746 100644 --- a/tools/gradle-plugin/testing-project/src/main/java/org/acme/Foo.java +++ b/tools/gradle-plugin/testing-project/src/main/java/org/acme/Foo.java @@ -1,10 +1,30 @@ package org.acme; +import io.smallrye.graphql.api.federation.FieldSet; +import io.smallrye.graphql.api.federation.Key; +@Key(fields = @FieldSet("id")) public class Foo { + private Integer id; private Integer number; + public Foo() { + } + + public Foo(Integer id, Integer number) { + this.id = id; + this.number = number; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + public Integer getNumber() { return number; } @@ -12,4 +32,4 @@ public Integer getNumber() { public void setNumber(Integer number) { this.number = number; } -} \ No newline at end of file +} diff --git a/tools/gradle-plugin/testing-project/src/main/java/org/acme/TestingApi.java b/tools/gradle-plugin/testing-project/src/main/java/org/acme/TestingApi.java index 7790f9183..54492c6d8 100644 --- a/tools/gradle-plugin/testing-project/src/main/java/org/acme/TestingApi.java +++ b/tools/gradle-plugin/testing-project/src/main/java/org/acme/TestingApi.java @@ -3,16 +3,10 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; -import java.util.Collections; -import java.util.List; - @GraphQLApi class TestingApi { - @Query public Foo getFoo() { return null; } - - -} \ No newline at end of file +}