diff --git a/client-subsystem/pom.xml b/client-subsystem/pom.xml new file mode 100644 index 0000000..b4018c4 --- /dev/null +++ b/client-subsystem/pom.xml @@ -0,0 +1,126 @@ + + + + + + wildfly-microprofile-graphql-parent + org.wildfly.extras.graphql + 2.4.1.Final-SNAPSHOT + + 2.4.1.Final-SNAPSHOT + 4.0.0 + + wildfly-microprofile-graphql-client + WildFly MicroProfile GraphQL - Client Subsystem + + + + io.smallrye + smallrye-graphql-servlet + + + io.smallrye + smallrye-graphql-cdi + + + io.smallrye + smallrye-graphql-schema-builder + + + io.smallrye + smallrye-graphql-schema-model + + + com.graphql-java + graphql-java + + + org.jboss.logging + jboss-logging-annotations + + + org.jboss.logging + jboss-logging-processor + + provided + true + + + org.wildfly.core + wildfly-controller + + + org.wildfly.core + wildfly-server + + + org.wildfly + wildfly-ee + + + org.jboss.metadata + jboss-metadata-web + + + org.wildfly + wildfly-web-common + + + org.wildfly + wildfly-weld + + + org.wildfly + wildfly-undertow + + + + io.smallrye + smallrye-graphql + + + jakarta.json.bind + jakarta.json.bind-api + + + jakarta.websocket + jakarta.websocket-api + + + jakarta.websocket + jakarta.websocket-client-api + + + + org.wildfly.core + wildfly-subsystem-test + pom + test + + + junit + junit + test + + + + diff --git a/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientExtension.java b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientExtension.java new file mode 100644 index 0000000..c861fcb --- /dev/null +++ b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientExtension.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.microprofile.graphql.client; + +import org.jboss.as.controller.ExtensionContext; +import org.jboss.as.controller.ModelVersion; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.SubsystemRegistration; +import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; +import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.parsing.ExtensionParsingContext; +import org.jboss.as.controller.registry.ManagementResourceRegistration; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; + +public class MicroProfileGraphQLClientExtension implements org.jboss.as.controller.Extension { + + public static final String SUBSYSTEM_NAME = "microprofile-graphql-client-smallrye"; + protected static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); + + protected static final ModelVersion VERSION_1_0_0 = ModelVersion.create(1, 0, 0); + private static final ModelVersion CURRENT_MODEL_VERSION = VERSION_1_0_0; + + private static final MicroProfileGraphQLClientParser_1_0 CURRENT_PARSER = new MicroProfileGraphQLClientParser_1_0(); + + static final String WELD_CAPABILITY_NAME = "org.wildfly.weld"; + static final String CONFIG_CAPABILITY_NAME = "org.wildfly.microprofile.config"; + + private static final String RESOURCE_NAME = MicroProfileGraphQLClientExtension.class.getPackage().getName() + ".LocalDescriptions"; + + @Override + public void initialize(ExtensionContext extensionContext) { + final SubsystemRegistration sr = extensionContext.registerSubsystem(SUBSYSTEM_NAME, CURRENT_MODEL_VERSION); + sr.registerXMLElementWriter(CURRENT_PARSER); + final ManagementResourceRegistration root = sr.registerSubsystemModel(new MicroProfileGraphQLClientSubsystemDefinition()); + root.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE, false); + } + + @Override + public void initializeParsers(ExtensionParsingContext extensionParsingContext) { + extensionParsingContext.setSubsystemXmlMapping(SUBSYSTEM_NAME, MicroProfileGraphQLClientParser_1_0.NAMESPACE, CURRENT_PARSER); + } + + static ResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) { + return getResourceDescriptionResolver(true, keyPrefix); + } + + static ResourceDescriptionResolver getResourceDescriptionResolver(final boolean useUnprefixedChildTypes, final String... keyPrefix) { + StringBuilder prefix = new StringBuilder(); + for (String kp : keyPrefix) { + if (prefix.length() > 0){ + prefix.append('.'); + } + prefix.append(kp); + } + return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, MicroProfileGraphQLClientExtension.class.getClassLoader(), true, useUnprefixedChildTypes); + } + +} diff --git a/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientParser_1_0.java b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientParser_1_0.java new file mode 100644 index 0000000..d35ce94 --- /dev/null +++ b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientParser_1_0.java @@ -0,0 +1,22 @@ +package org.wildfly.extension.microprofile.graphql.client; + +import org.jboss.as.controller.PersistentResourceXMLDescription; +import org.jboss.as.controller.PersistentResourceXMLParser; + +public class MicroProfileGraphQLClientParser_1_0 extends PersistentResourceXMLParser { + + public static final String NAMESPACE = "urn:wildfly:microprofile-graphql-client-smallrye:1.0"; + + private static final PersistentResourceXMLDescription xmlDescription; + + static { + xmlDescription = org.jboss.as.controller.PersistentResourceXMLDescription.builder(MicroProfileGraphQLClientExtension.SUBSYSTEM_PATH, NAMESPACE) + .build(); + } + + @Override + public PersistentResourceXMLDescription getParserDescription() { + return xmlDescription; + } + +} diff --git a/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientSubsystemDefinition.java b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientSubsystemDefinition.java new file mode 100644 index 0000000..05f0737 --- /dev/null +++ b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/MicroProfileGraphQLClientSubsystemDefinition.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.microprofile.graphql.client; + +import org.jboss.as.controller.AbstractBoottimeAddStepHandler; +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ModelOnlyRemoveStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.PersistentResourceDefinition; +import org.jboss.as.controller.capability.RuntimeCapability; +import org.jboss.as.server.AbstractDeploymentChainStep; +import org.jboss.as.server.DeploymentProcessorTarget; +import org.jboss.as.server.deployment.Phase; +import org.jboss.dmr.ModelNode; +//import org.wildfly.extension.microprofile.graphql._private.MicroProfileGraphQLLogger; +import org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLClientDependencyProcessor; +import org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLClientDeploymentProcessor; + +import java.util.Collection; +import java.util.Collections; + +import static org.wildfly.extension.microprofile.graphql.client.MicroProfileGraphQLClientExtension.CONFIG_CAPABILITY_NAME; +import static org.wildfly.extension.microprofile.graphql.client.MicroProfileGraphQLClientExtension.SUBSYSTEM_NAME; +import static org.wildfly.extension.microprofile.graphql.client.MicroProfileGraphQLClientExtension.SUBSYSTEM_PATH; +import static org.wildfly.extension.microprofile.graphql.client.MicroProfileGraphQLClientExtension.WELD_CAPABILITY_NAME; + +public class MicroProfileGraphQLClientSubsystemDefinition extends PersistentResourceDefinition { + + private static final String GRAPHQL_CAPABILITY_NAME = "org.wildfly.microprofile.graphql"; + + private static final RuntimeCapability GRAPHQL_CAPABILITY = RuntimeCapability.Builder + .of(GRAPHQL_CAPABILITY_NAME) + .addRequirements(WELD_CAPABILITY_NAME) + .addRequirements(CONFIG_CAPABILITY_NAME) + .build(); + + public MicroProfileGraphQLClientSubsystemDefinition() { + super( + new Parameters( + SUBSYSTEM_PATH, + MicroProfileGraphQLClientExtension.getResourceDescriptionResolver(SUBSYSTEM_NAME)) + .setAddHandler(AddHandler.INSTANCE) + .setRemoveHandler(new ModelOnlyRemoveStepHandler()) + .setCapabilities(GRAPHQL_CAPABILITY) + ); + } + + @Override + public Collection getAttributes() { + return Collections.emptyList(); + } + + static class AddHandler extends AbstractBoottimeAddStepHandler { + + static AddHandler INSTANCE = new AddHandler(); + + private AddHandler() { + super(Collections.emptyList()); + } + + @Override + protected void performBoottime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performBoottime(context, operation, model); + + context.addStep(new AbstractDeploymentChainStep() { + public void execute(DeploymentProcessorTarget processorTarget) { + final int DEPENDENCIES_MICROPROFILE_GRAPHQL = 6288; + processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME, Phase.DEPENDENCIES, DEPENDENCIES_MICROPROFILE_GRAPHQL, new MicroProfileGraphQLClientDependencyProcessor()); + final int POST_MODULE_MICROPROFILE_GRAPHQL = 14241; + processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME, Phase.POST_MODULE, POST_MODULE_MICROPROFILE_GRAPHQL, new MicroProfileGraphQLClientDeploymentProcessor()); + } + }, OperationContext.Stage.RUNTIME); + +// MicroProfileGraphQLLogger.LOGGER.activatingSubsystem(); + } + } +} diff --git a/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDependencyProcessor.java b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDependencyProcessor.java new file mode 100644 index 0000000..96025b2 --- /dev/null +++ b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDependencyProcessor.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.microprofile.graphql.client.deployment; +// org.wildfly.extension.microprofile.graphql-client-smallrye + +import org.jboss.as.server.deployment.Attachments; +import org.jboss.as.server.deployment.DeploymentPhaseContext; +import org.jboss.as.server.deployment.DeploymentUnit; +import org.jboss.as.server.deployment.DeploymentUnitProcessingException; +import org.jboss.as.server.deployment.DeploymentUnitProcessor; +import org.jboss.as.server.deployment.annotation.CompositeIndex; +import org.jboss.logging.Logger; + +import static org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLClientDeploymentProcessor.GRAPHQL_CLIENT_API; + +public class MicroProfileGraphQLClientDependencyProcessor implements DeploymentUnitProcessor { + + static final Logger LOG = Logger.getLogger(MicroProfileGraphQLClientDependencyProcessor.class); + + @Override + public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { + final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); + final CompositeIndex compositeIndex = deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX); + if(!compositeIndex.getAnnotations(GRAPHQL_CLIENT_API).isEmpty()) { + LOG.info("GRAPHQL CLIENT API FOUND"); +// final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION); +// final ModuleLoader moduleLoader = Module.getBootModuleLoader(); +// ModuleDependency dependency = new ModuleDependency(moduleLoader, "io.smallrye.graphql.client.vertx", false, false, true, false); +// this is an equivalent of meta-inf="import" in jboss-deployment-structure.xml and is needed to be able to see CDI beans from the module +// dependency.addImportFilter(s -> s.equals("META-INF"), true); +// moduleSpecification.addSystemDependency(dependency); + } + } + + @Override + public void undeploy(DeploymentUnit context) { + + } +} diff --git a/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDeploymentProcessor.java b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDeploymentProcessor.java new file mode 100644 index 0000000..18f7375 --- /dev/null +++ b/client-subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLClientDeploymentProcessor.java @@ -0,0 +1,155 @@ +/* + * Copyright 2020 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.microprofile.graphql.client.deployment; + +import io.smallrye.graphql.entry.http.ExecutionServlet; +import io.smallrye.graphql.entry.http.SchemaServlet; +import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.jboss.as.ee.structure.DeploymentType; +import org.jboss.as.ee.structure.DeploymentTypeMarker; +import org.jboss.as.server.deployment.Attachments; +import org.jboss.as.server.deployment.DeploymentPhaseContext; +import org.jboss.as.server.deployment.DeploymentUnit; +import org.jboss.as.server.deployment.DeploymentUnitProcessingException; +import org.jboss.as.server.deployment.DeploymentUnitProcessor; +import org.jboss.as.server.deployment.annotation.CompositeIndex; +import org.jboss.as.web.common.WarMetaData; +import org.jboss.jandex.DotName; +import org.jboss.logging.Logger; +import org.jboss.metadata.web.jboss.JBossServletMetaData; +import org.jboss.metadata.web.jboss.JBossServletsMetaData; +import org.jboss.metadata.web.jboss.JBossWebMetaData; +import org.jboss.metadata.web.spec.ListenerMetaData; +import org.jboss.metadata.web.spec.ServletMappingMetaData; +//import org.wildfly.extension.microprofile.graphql.WildFlyGraphQLServerWebSocket; +//import org.wildfly.extension.microprofile.graphql._private.MicroProfileGraphQLLogger; +import org.wildfly.extension.undertow.deployment.UndertowAttachments; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MicroProfileGraphQLClientDeploymentProcessor implements DeploymentUnitProcessor { + + static final DotName GRAPHQL_CLIENT_API = DotName + .createSimple("io.smallrye.graphql.client.typesafe.api.GraphQLClientApi"); + + static final Logger LOG = Logger.getLogger(MicroProfileGraphQLClientDeploymentProcessor.class); + + @Override + public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { + LOG.info("LETS GOOOO"); +// final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); +// if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) { +// return; +// } +// +// // see whether this deployment contains GraphQL annotations +// final CompositeIndex compositeIndex = deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX); +// if (compositeIndex.getAnnotations(ANNOTATION_GRAPHQL_API).isEmpty()) { +// return; +// } +// MicroProfileGraphQLLogger.LOGGER.activatingGraphQLForDeployment(deploymentUnit.getName()); +// +// // Scan for GraphQL Federation annotations. If found, automatically activate federation. +// if(hasFederationAnnotations(compositeIndex)) { +// System.setProperty("smallrye.graphql.federation.enabled", "true"); +// } +// +// +// // steps needed for application initialization +// JBossWebMetaData mergedJBossWebMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY).getMergedJBossWebMetaData(); +// registerStartupListener(mergedJBossWebMetaData); +// registerExecutionServlet(mergedJBossWebMetaData); +// registerSchemaServlet(mergedJBossWebMetaData); +// +// // if the GraphQL API contains subscriptions, deploy the relevant web socket endpoint that handles them +// if (!compositeIndex.getAnnotations(ANNOTATION_SUBSCRIPTION).isEmpty()) { +// WebSocketDeploymentInfo webSocketDeploymentInfo = deploymentUnit.getAttachment(UndertowAttachments.WEB_SOCKET_DEPLOYMENT_INFO); +// webSocketDeploymentInfo.addEndpoint(WildFlyGraphQLServerWebSocket.class); +// mergedJBossWebMetaData.setEnableWebSockets(true); +// } +// + } +// +// private boolean hasFederationAnnotations(CompositeIndex index) { +// return !index.getAnnotations(ANNOTATION_FEDERATION_EXTENDS).isEmpty() || +// !index.getAnnotations(ANNOTATION_FEDERATION_EXTERNAL).isEmpty() || +// !index.getAnnotations(ANNOTATION_FEDERATION_KEY).isEmpty() || +// !index.getAnnotations(ANNOTATION_FEDERATION_PROVIDES).isEmpty() || +// !index.getAnnotations(ANNOTATION_FEDERATION_REQUIRES).isEmpty(); +// } +// +// +// // register the io.smallrye.graphql.servlet.StartupListener which needs to be called to initialize +// // the application +// private void registerStartupListener(JBossWebMetaData webdata) { +// ListenerMetaData startupListenerMetadata = new ListenerMetaData(); +// startupListenerMetadata.setListenerClass("io.smallrye.graphql.entry.http.StartupListener"); +// List containerListeners = webdata.getListeners(); +// if (containerListeners == null) { +// List list = new ArrayList<>(); +// list.add(startupListenerMetadata); +// webdata.setListeners(list); +// } else { +// containerListeners.add(startupListenerMetadata); +// } +// } +// +// private void registerExecutionServlet(JBossWebMetaData webdata) { +// JBossServletMetaData servlet = new JBossServletMetaData(); +// servlet.setLoadOnStartup("1"); +// servlet.setName("SmallRyeGraphQLExecutionServlet"); +// servlet.setServletClass(ExecutionServlet.class.getName()); +// servlet.setAsyncSupported(false); +// +// if (webdata.getServlets() == null) { +// webdata.setServlets(new JBossServletsMetaData()); +// } +// webdata.getServlets().add(servlet); +// ServletMappingMetaData mapping = new ServletMappingMetaData(); +// mapping.setServletName("SmallRyeGraphQLExecutionServlet"); +// mapping.setUrlPatterns(Collections.singletonList("/graphql")); // TODO make configurable +// List mappings = webdata.getServletMappings(); +// if (mappings != null) { +// mappings.add(mapping); +// } else { +// mappings = new ArrayList<>(); +// mappings.add(mapping); +// webdata.setServletMappings(mappings); +// } +// +// } +// +// private void registerSchemaServlet(JBossWebMetaData webdata) { +// JBossServletMetaData servlet = new JBossServletMetaData(); +// servlet.setLoadOnStartup("2"); +// servlet.setName("SmallRyeGraphQLSchemaServlet"); +// servlet.setServletClass(SchemaServlet.class.getName()); +// servlet.setAsyncSupported(false); +// webdata.getServlets().add(servlet); +// ServletMappingMetaData mapping = new ServletMappingMetaData(); +// mapping.setServletName("SmallRyeGraphQLSchemaServlet"); +// mapping.setUrlPatterns(Collections.singletonList("/graphql/schema.graphql")); // TODO make configurable +// webdata.getServletMappings().add(mapping); +// } + + @Override + public void undeploy(DeploymentUnit deploymentUnit) { + } + +} diff --git a/client-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/client-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension new file mode 100644 index 0000000..c37b046 --- /dev/null +++ b/client-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension @@ -0,0 +1,17 @@ +# +# Copyright 2019 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.wildfly.extension.microprofile.graphql.client.MicroProfileGraphQLClientExtension \ No newline at end of file diff --git a/client-subsystem/src/main/resources/org/wildfly/extension/microprofile/graphql/LocalDescriptions.properties b/client-subsystem/src/main/resources/org/wildfly/extension/microprofile/graphql/LocalDescriptions.properties new file mode 100644 index 0000000..ec40e9e --- /dev/null +++ b/client-subsystem/src/main/resources/org/wildfly/extension/microprofile/graphql/LocalDescriptions.properties @@ -0,0 +1,19 @@ +# +# Copyright 2019 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +microprofile-graphql-client-smallrye=The Microprofile GraphQL Client subsystem implemented by SmallRye +microprofile-graphql-client-smallrye.add=Adds the Microprofile GraphQL Client subsystem +microprofile-graphql-client-smallrye.remove=Removes the Microprofile GraphQL Client subsystem \ No newline at end of file diff --git a/client-subsystem/src/main/resources/schema/wildfly-microprofile-graphql-client-smallrye_1_0.xsd b/client-subsystem/src/main/resources/schema/wildfly-microprofile-graphql-client-smallrye_1_0.xsd new file mode 100644 index 0000000..16e58bf --- /dev/null +++ b/client-subsystem/src/main/resources/schema/wildfly-microprofile-graphql-client-smallrye_1_0.xsd @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/feature-pack/pom.xml b/feature-pack/pom.xml index d679ef3..a7e9067 100644 --- a/feature-pack/pom.xml +++ b/feature-pack/pom.xml @@ -138,6 +138,12 @@ provided + + ${project.groupId} + wildfly-microprofile-graphql-client + provided + + io.smallrye diff --git a/feature-pack/src/main/resources/feature_groups/graphql-client.xml b/feature-pack/src/main/resources/feature_groups/graphql-client.xml new file mode 100644 index 0000000..2974011 --- /dev/null +++ b/feature-pack/src/main/resources/feature_groups/graphql-client.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/feature-pack/src/main/resources/layers/standalone/microprofile-graphql/layer-spec.xml b/feature-pack/src/main/resources/layers/standalone/microprofile-graphql/layer-spec.xml index 6175261..614690d 100644 --- a/feature-pack/src/main/resources/layers/standalone/microprofile-graphql/layer-spec.xml +++ b/feature-pack/src/main/resources/layers/standalone/microprofile-graphql/layer-spec.xml @@ -26,9 +26,11 @@ + + diff --git a/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/microprofile/graphql-client-smallrye/main/module.xml b/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/microprofile/graphql-client-smallrye/main/module.xml new file mode 100644 index 0000000..35881a4 --- /dev/null +++ b/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/microprofile/graphql-client-smallrye/main/module.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feature-pack/wildfly-feature-pack-build.xml b/feature-pack/wildfly-feature-pack-build.xml index 83ac96c..984e80d 100644 --- a/feature-pack/wildfly-feature-pack-build.xml +++ b/feature-pack/wildfly-feature-pack-build.xml @@ -51,6 +51,7 @@ org.wildfly.extension.microprofile.graphql-smallrye + org.wildfly.extension.microprofile.graphql-client-smallrye diff --git a/pom.xml b/pom.xml index b75afb3..1777e9a 100644 --- a/pom.xml +++ b/pom.xml @@ -456,6 +456,17 @@ + + ${project.groupId} + wildfly-microprofile-graphql-client + ${project.version} + + + * + * + + + ${project.groupId} wildfly-microprofile-graphql-feature-pack @@ -685,6 +696,9 @@ build testsuite quickstart + quickstart-client + client-subsystem + diff --git a/quickstart-client/README.md b/quickstart-client/README.md new file mode 100644 index 0000000..fe7b98c --- /dev/null +++ b/quickstart-client/README.md @@ -0,0 +1,251 @@ +# GraphQL quickstart + +## Prerequisites +- JDK 1.8+ +- Maven + + +## Building and deploying the quickstart + +The [main README](../README.md) contains information about the layers in this feature pack. You can use the `wildfly-maven-plugin` to build and run the server with the feature pack, and deploy the quickstart war. + +``` +mvn wildfly:provision wildfly:dev +``` + +## Check the GraphQL schema +To view the schema, execute this command: +``` +curl localhost:8080/quickstart/graphql/schema.graphql +``` + +## Experimenting with queries using GraphiQL +GraphiQL is a UI tool that can be used to execute GraphQL queries. Open GraphiQL by navigating to +`http://localhost:8080/quickstart/graphql-ui` in your browser. Queries that you should try out are listed below. +You will probably also notice that GraphiQL, based on the knowledge of the GraphQL schema, offers autocompletion! + +### Queries +Enter the following query to GraphiQL and press the play button: + +``` +query allFilms { + allFilms { + title + director + releaseDate + episodeID + } +} +``` + +Note: The equivalent query can be executed via curl by +``` +curl -X POST localhost:8080/quickstart/graphql -d'{"query": "{allFilms{title director releaseDate episodeID}}"}' +``` + + +Since our query contains all the fields in the Film class we will retrieve all the fields in our response. +Since GraphQL API responses are client determined, the client can choose which fields it will require. + +Let’s assume that our client only requires `title` and `releaseDate` making the previous call to the API +Over-fetching of unnecessary data. + +Enter the following query into GraphiQL and hit the play button: + +``` +query allFilms { + allFilms { + title + releaseDate + } +} +``` + +Notice in the response we have only retrieved the required fields. Therefore, we have prevented Over-fetching. + +Let's now try out a way how to retrieve one film my its id. It's implemented by this query: +``` +@Query +@Description("Get a Films from a galaxy far far away") +public Film getFilm(@Name("filmId") int id) { + return service.getFilm(id); +} +``` + +Enter the following into GraphiQL and make a request. + +``` +query getFilm { + film(filmId: 1) { + title + director + releaseDate + episodeID + } +} +``` + +The film query method requested fields can be determined as such in our previous example. +This way we can retrieve individual film information. + +However, say our client requires both films with filmId 0 and 1. In a REST API the client would have to make +two calls to the API. Therefore, the client would be Under-fetching. + +In GraphQL it is possible to make multiple queries at once. + +Enter the following into GraphiQL to retrieve two films: + +``` +query getFilms { + film0: film(filmId: 0) { + title + director + releaseDate + episodeID + } + film1: film(filmId: 1) { + title + director + releaseDate + episodeID + } +} +``` + +This enabled the client to fetch the required data in a single request. + +Until now, we have created a GraphQL API to retrieve film data. +We now want to enable the clients to retrieve the Hero data of the Film. + +Review the following source-query in our `FilmResource` class: + +``` +public List heroes(@Source Film film) { + return service.getHeroesByFilm(film); +} +``` +Enter the following into GraphiQL to retrieve the film and hero data. + +``` +query getFilmHeroes { + film(filmId: 1) { + title + director + releaseDate + episodeID + heroes { + name + height + mass + darkSide + lightSaber + } + } +} +``` + +The response now includes the heroes of the film. + +### Mutations + +Mutations are used when data is created, updated or deleted. +Review the following mutations in our `FilmResource` class: + +``` +@Mutation +public Hero createHero(Hero hero) { + service.addHero(hero); + return hero; +} + +@Mutation +public Hero deleteHero(int id) { + return service.deleteHero(id); +} +``` + +Enter the following into GraphiQL to insert a Hero: + +``` +mutation addHero { + createHero(hero: { + name: "Han", + surname: "Solo" + height: 1.85 + mass: 80 + darkSide: false + episodeIds: [4, 5, 6] + } + ) + { + name + surname + } +} +``` + +By using this mutation we have created a Hero entity in our service. + +Notice how in the response we have retrieved the name and surname of the created Hero. +This is because we selected to retrieve these fields in the response within the `{ }` +in the mutation query. This can easily be a server side generated field that the client may require. + +Let’s now try deleting an entry: + +``` +mutation DeleteHero { + deleteHero(id :3){ + name + surname + } +} +``` + +Similar to the `createHero` mutation method we also retrieve the name and surname of the hero +we have deleted which is defined in { }. + +### Creating Queries by fields + +Queries can also be done on individual fields. For example, let’s create a method to +query heroes by their last name. + +Review the following snippet from the FilmResource class: + +``` +@Query +public List getHeroesWithSurname(@DefaultValue("Skywalker") String surname) { + return service.getHeroesBySurname(surname); +} +``` + +By using the `@DefaultValue` annotation we have determined that the surname value +will be Skywalker when the parameter is not provided. + +Test the following queries with GraphiQL: + +``` +query heroWithDefaultSurname { + heroesWithSurname{ + name + surname + lightSaber + } +} +query heroWithSurnames { + heroesWithSurname(surname: "Vader") { + name + surname + lightSaber + } +} +``` + +## Conclusion +MicroProfile GraphQL enables clients to retrieve the exact data that is required preventing +Over-fetching and Under-fetching. + +The GraphQL API can be expanded without breaking previous queries enabling easy API evolution. + + + + diff --git a/quickstart-client/pom.xml b/quickstart-client/pom.xml new file mode 100644 index 0000000..2bf3519 --- /dev/null +++ b/quickstart-client/pom.xml @@ -0,0 +1,250 @@ + + + + 4.0.0 + + + wildfly-microprofile-graphql-parent + org.wildfly.extras.graphql + 2.4.1.Final-SNAPSHOT + + + wildfly-microprofile-graphql-quickstart-client + 2.4.1.Final-SNAPSHOT + war + WildFly MicroProfile GraphQL - Quickstart Client + Quickstart for the WildFly implementation of MicroProfile GraphQL Client + + + true + + + ${project.basedir}/target/wildfly + ${jboss.dist} + + 127.0.0.2 + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + + + + + io.smallrye + smallrye-graphql-client-api + ${version.io.smallrye.graphql} + + + io.smallrye + smallrye-graphql-client + ${version.io.smallrye.graphql} + + + + + + + io.smallrye + smallrye-graphql-client-api + provided + + + org.eclipse.microprofile.graphql + microprofile-graphql-api + provided + + + io.smallrye + smallrye-graphql-api + provided + + + io.smallrye.reactive + mutiny + provided + + + org.reactivestreams + reactive-streams + provided + + + + jakarta.json + jakarta.json-api + provided + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + jakarta.inject + jakarta.inject-api + provided + + + + + io.smallrye + smallrye-graphql-ui-graphiql + compile + + + + + + + jakarta.validation + jakarta.validation-api + provided + + + + + org.wildfly.arquillian + wildfly-arquillian-container-managed + test + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + junit + junit + test + + + org.wildfly.arquillian + wildfly-arquillian-protocol-jmx + test + + + + + quickstart-client + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + wildfly@maven(org.jboss.universe:community-universe):current#${version.org.wildfly} + + + org.wildfly.extras.graphql:wildfly-microprofile-graphql-feature-pack:${project.version} + + + + cloud-server + jmx-remoting + management + microprofile-graphql + micrometer + microprofile-telemetry + + + + + + + org.jboss.galleon + galleon-maven-plugin + + + server-provisioning + + provision + + test-compile + + ${project.build.directory}/wildfly-test + false + ${galleon.log.time} + + + ${galleon.fork.embedded} + passive+ + + + + true + org.wildfly + wildfly-galleon-pack + ${version.org.wildfly} + false + false + + + ${project.groupId} + wildfly-microprofile-graphql-feature-pack + ${project.version} + false + false + + + + + standalone + standalone.xml + + jaxrs-server + jmx-remoting + observability + + microprofile-graphql + micrometer + microprofile-telemetry + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.build.directory}/wildfly-test + -Djboss.bind.address=${node0} -Djboss.bind.address.management=${node0} -Djboss.bind.address.unsecure=${node0} + ${node0} + + + + + + + diff --git a/quickstart-client/provision.xml b/quickstart-client/provision.xml new file mode 100644 index 0000000..93e27af --- /dev/null +++ b/quickstart-client/provision.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/Film.java b/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/Film.java new file mode 100644 index 0000000..f32deb0 --- /dev/null +++ b/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/Film.java @@ -0,0 +1,44 @@ +package org.wildfly.extras.quickstart.microprofile.graphql; + +import java.time.LocalDate; + +public class Film { + + private String title; + private Integer episodeID; + private String director; + private LocalDate releaseDate; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Integer getEpisodeID() { + return episodeID; + } + + public void setEpisodeID(Integer episodeID) { + this.episodeID = episodeID; + } + + public String getDirector() { + return director; + } + + public void setDirector(String director) { + this.director = director; + } + + public LocalDate getReleaseDate() { + return releaseDate; + } + + public void setReleaseDate(LocalDate releaseDate) { + this.releaseDate = releaseDate; + } + +} \ No newline at end of file diff --git a/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/FilmClientApi.java b/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/FilmClientApi.java new file mode 100644 index 0000000..17a9199 --- /dev/null +++ b/quickstart-client/src/main/java/org/wildfly/extras/quickstart/microprofile/graphql/FilmClientApi.java @@ -0,0 +1,9 @@ +package org.wildfly.extras.quickstart.microprofile.graphql; + +import java.util.List; +import io.smallrye.graphql.client.typesafe.api.GraphQLClientApi; + +@GraphQLClientApi +interface FilmClientApi { + List allFilms(); +} diff --git a/quickstart-client/src/main/resources/META-INF/microprofile-config.properties b/quickstart-client/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..20934fd --- /dev/null +++ b/quickstart-client/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,5 @@ +# Example configuration file for a GraphQL application for WildFly +# for all config options, see SmallRye GraphQL documentation + +# print exceptions that occurred during data fetching +smallrye.graphql.printDataFetcherException=true diff --git a/quickstart-client/src/main/webapp/WEB-INF/beans.xml b/quickstart-client/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000..ffdfc41 --- /dev/null +++ b/quickstart-client/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/quickstart-client/src/main/webapp/WEB-INF/web.xml b/quickstart-client/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..2ab35d4 --- /dev/null +++ b/quickstart-client/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/quickstart/pom.xml b/quickstart/pom.xml index e692ab8..9263f9e 100644 --- a/quickstart/pom.xml +++ b/quickstart/pom.xml @@ -38,7 +38,7 @@ - 127.0.0.2 + 127.0.0.1 diff --git a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/MicroProfileGraphQLSubsystemDefinition.java b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/MicroProfileGraphQLSubsystemDefinition.java index eb87ac4..f6953f5 100644 --- a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/MicroProfileGraphQLSubsystemDefinition.java +++ b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/MicroProfileGraphQLSubsystemDefinition.java @@ -29,9 +29,9 @@ import org.jboss.as.server.deployment.Phase; import org.jboss.dmr.ModelNode; import org.wildfly.extension.microprofile.graphql._private.MicroProfileGraphQLLogger; -import org.wildfly.extension.microprofile.graphql.deployment.GraphiQLUIDeploymentProcessor; -import org.wildfly.extension.microprofile.graphql.deployment.MicroProfileGraphQLDependencyProcessor; -import org.wildfly.extension.microprofile.graphql.deployment.MicroProfileGraphQLDeploymentProcessor; +import org.wildfly.extension.microprofile.graphql.client.deployment.GraphiQLUIDeploymentProcessor; +import org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLDependencyProcessor; +import org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLDeploymentProcessor; import java.util.Collection; import java.util.Collections; diff --git a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/GraphiQLUIDeploymentProcessor.java b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/GraphiQLUIDeploymentProcessor.java similarity index 98% rename from subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/GraphiQLUIDeploymentProcessor.java rename to subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/GraphiQLUIDeploymentProcessor.java index 391f32a..e6b92e3 100644 --- a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/GraphiQLUIDeploymentProcessor.java +++ b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/GraphiQLUIDeploymentProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.extension.microprofile.graphql.deployment; +package org.wildfly.extension.microprofile.graphql.client.deployment; import org.jboss.as.ee.component.EEModuleDescription; import org.jboss.as.ee.structure.DeploymentType; diff --git a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDependencyProcessor.java b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDependencyProcessor.java similarity index 91% rename from subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDependencyProcessor.java rename to subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDependencyProcessor.java index 4a59e1c..be3f46e 100644 --- a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDependencyProcessor.java +++ b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDependencyProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.extension.microprofile.graphql.deployment; +package org.wildfly.extension.microprofile.graphql.client.deployment; import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentPhaseContext; @@ -27,7 +27,7 @@ import org.jboss.modules.Module; import org.jboss.modules.ModuleLoader; -import static org.wildfly.extension.microprofile.graphql.deployment.MicroProfileGraphQLDeploymentProcessor.ANNOTATION_GRAPHQL_API; +import static org.wildfly.extension.microprofile.graphql.client.deployment.MicroProfileGraphQLDeploymentProcessor.ANNOTATION_GRAPHQL_API; public class MicroProfileGraphQLDependencyProcessor implements DeploymentUnitProcessor { diff --git a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDeploymentProcessor.java b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDeploymentProcessor.java similarity index 99% rename from subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDeploymentProcessor.java rename to subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDeploymentProcessor.java index 638da22..9e04ba6 100644 --- a/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/deployment/MicroProfileGraphQLDeploymentProcessor.java +++ b/subsystem/src/main/java/org/wildfly/extension/microprofile/graphql/client/deployment/MicroProfileGraphQLDeploymentProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.extension.microprofile.graphql.deployment; +package org.wildfly.extension.microprofile.graphql.client.deployment; import io.smallrye.graphql.entry.http.ExecutionServlet; import io.smallrye.graphql.entry.http.SchemaServlet;