Skip to content

Commit

Permalink
new quickstart for smallrye graphql client
Browse files Browse the repository at this point in the history
  • Loading branch information
mskacelik committed Sep 4, 2024
1 parent 2353269 commit d60bbf8
Show file tree
Hide file tree
Showing 12 changed files with 731 additions and 0 deletions.
145 changes: 145 additions & 0 deletions quickstart-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# GraphQL Client quickstart

## Prerequisites
- JDK 11+
- Maven

## Quickstart's compatibility with server-side quickstart
This quickstart requires the server-side quickstart implementation to function properly. Instructions for deploying the feature pack on the server-side can be found in the server-side quickstart's [README](../quickstart/README.md).


## Building and deployment

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
```


## Functionality
The quickstart-client.war application exposes various REST endpoints that demonstrate interacting with the server-side GraphQL API through the SmallRye GraphQL Client.
The endpoints utilize both typesafe and dynamic client approaches.
### Typesafe API

The typesafe client functions like a MicroProfile REST Client but is tailored for GraphQL endpoints. To use it, you will need domain model classes that match the GraphQL schema.

Think of a client instance as a proxy. You interact with it as you would a regular Java object, and it translates your calls into GraphQL operations.

It directly handles domain classes, translating input and output data between Java objects and their representations in the GraphQL query language.
#### Example

Let's start by defining a domain class `Film` to represent the data we will be working with.
```java
package org.wildfly.extras.quickstart.microprofile.graphql.client;

import java.time.LocalDate;

public class Film {
String title;
Integer episodeID;
String director;
LocalDate releaseDate;
String desc;
}
```
Next, create a GraphQL client API interface `FilmClientApi` to define the GraphQL operations:
```java
@GraphQLClientApi
interface FilmClientApi {
@Query
List<Film> getAllFilms();
}
```

Finally, implement a REST endpoint to expose the GraphQL client's functionality:
```java
@GET
@Path("/typesafe/films")
@Produces(MediaType.APPLICATION_JSON)
public List<Film> getAllFilms() {
FilmClientApi client = TypesafeGraphQLClientBuilder.newBuilder()
.endpoint(URL) // http://localhost:8080/quickstart/graphql
.build(FilmClientApi.class);
return client.getAllFilms();
}
```
You can test the endpoint using a tool like `curl`:
```
curl localhost:8080/quickstart-client/typesafe/films
```
The typesafe client will automatically generate the corresponding GraphQL query based on the operation's return type and domain classes:
```
query allFilms {
allFilms {
title
pisodeID
director
releaseDate
desc
}
}
```
The proxy will then send this query as part of the GraphQL request to the server. The server will process the request and return a GraphQL response.
The response will be deserialized into Java objects.

Other endpoints:
- **`localhost:8080/typesafe/films/{id}`:** Retrieves a specific film by its index.
- **`localhost:8080/typesafe/delete/hero/{id}`:** Deletes a hero by its index.
- **`localhost:8080/typesafe/heroes/{surname}`:** Retrieves a list of heroes with a specific surname.

> [NOTE]
> The `VertxTypesafeGraphQLClientBuilder` (*Vert.x*'s typesafe implementation) allows to inject a pre-build `ClientModels` bean instance–which has been generated during deployment process via Jandex API.
> This means you can provide the pre-generated GraphQL queries directly to the typesafe client builder, bypassing the default generation process of using Java Reflection during runtime.
> ```java
> @Inject
> ClientModels clientmodels;
> // ...
> FilmClientApi client = new VertxTypesafeGraphQLClientBuilder()
> .clientModels(clientModels)
> .endpoint(URL)
> .build(FilmClientApi.class);
> // ...
>```
### Dynamic API
Unlike the typesafe API, the dynamic client does not require a client API interface or domain classes. It operates directly with abstract representations of GraphQL documents, constructed using a domain-specific language (DSL).
Exchanged objects are treated as abstract `JsonObject`, but can be converted to concrete model objects if necessary.
#### Example
```java
@GET
@Path("/dynamic/films/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Film getFilmDynamic(@PathParam("id") int id) throws ExecutionException, InterruptedException {
try (VertxDynamicGraphQLClient dynamicGraphQLClient =
(VertxDynamicGraphQLClient) new VertxDynamicGraphQLClientBuilder()
.url(URL)
.build()
) {
Variable filmId = var("filmId", nonNull(ScalarType.GQL_INT));
Document query = document(
operation("film", vars(filmId),
field("film", args(arg("filmId", filmId)),
field("title"),
field("episodeID"),
field("director"),
field("releaseDate"),
field("desc")
)
)
);
Response response = dynamicGraphQLClient.executeSync(query, Collections.singletonMap("filmId", id));
return response.getObject(Film.class, "film");
}
}
```
You can test the endpoint using a tool like `curl`:
```
curl localhost:8080/quickstart-client/dynamic/films/{id}
```
209 changes: 209 additions & 0 deletions quickstart-client/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>wildfly-microprofile-graphql-parent</artifactId>
<groupId>org.wildfly.extras.graphql</groupId>
<version>2.4.2.Final-SNAPSHOT</version>
</parent>

<artifactId>wildfly-microprofile-graphql-quickstart-client</artifactId>
<version>2.4.2.Final-SNAPSHOT</version>
<packaging>war</packaging>
<name>WildFly MicroProfile GraphQL - Quickstart Client</name>
<description>Quickstart for the WildFly implementation of MicroProfile GraphQL Client</description>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<!-- these are only relevant for testing -->
<jboss.dist>${project.basedir}/target/wildfly</jboss.dist>
<jboss.home>${jboss.dist}</jboss.home>
<!-- Where the testing WF instance will be bound.
If the user has already started their WF instance for experimenting with the quickstart, it is most likely
on 127.0.0.1, so let's try 127.0.0.2 to avoid clashing and run a separate instance. -->
<node0>127.0.0.2</node0>
</properties>

<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>

<!-- just override dependencyManagement from parent because we don't want to exclude transitive deps
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-api</artifactId>
<version>${version.io.smallrye.graphql}</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client</artifactId>
<version>${version.io.smallrye.graphql}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-implementation-vertx</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-model</artifactId>
<version>${version.io.smallrye.graphql}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.graphql</groupId>
<artifactId>microprofile-graphql-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>quickstart-client</finalName>
<plugins>
<plugin>
<!-- To be able to deploy the app by doing mvn package wildfly:deploy -->
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<configuration>
<feature-packs>
<feature-pack>
<location>wildfly@maven(org.jboss.universe:community-universe):current#${version.org.wildfly}</location>
</feature-pack>
<feature-pack>
<location>org.wildfly.extras.graphql:wildfly-microprofile-graphql-feature-pack:${project.version}</location>
</feature-pack>
</feature-packs>
<layers>
<layer>cloud-server</layer>
<layer>jmx-remoting</layer>
<layer>management</layer>
<layer>microprofile-graphql</layer>
<layer>micrometer</layer>
<layer>microprofile-telemetry</layer>
</layers>
</configuration>
</plugin>

<!-- Prepare a WildFly distribution for testing. Only relevant for the tests. -->
<plugin>
<groupId>org.jboss.galleon</groupId>
<artifactId>galleon-maven-plugin</artifactId>
<executions>
<execution>
<id>server-provisioning</id>
<goals>
<goal>provision</goal>
</goals>
<phase>test-compile</phase>
<configuration>
<install-dir>${project.build.directory}/wildfly-test</install-dir>
<record-state>false</record-state>
<log-time>${galleon.log.time}</log-time>
<plugin-options>
<jboss-maven-dist/>
<jboss-fork-embedded>${galleon.fork.embedded}</jboss-fork-embedded>
<optional-packages>passive+</optional-packages>
</plugin-options>
<feature-packs>
<feature-pack>
<transitive>true</transitive>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-galleon-pack</artifactId>
<version>${version.org.wildfly}</version>
<inherit-packages>false</inherit-packages>
<inherit-configs>false</inherit-configs>
</feature-pack>
<feature-pack>
<groupId>${project.groupId}</groupId>
<artifactId>wildfly-microprofile-graphql-feature-pack</artifactId>
<version>${project.version}</version>
<inherit-packages>false</inherit-packages>
<inherit-configs>false</inherit-configs>
</feature-pack>
</feature-packs>
<configurations>
<config>
<model>standalone</model>
<name>standalone.xml</name>
<layers>
<layer>jaxrs-server</layer>
<layer>jmx-remoting</layer>
<layer>observability</layer>
<!-- Layers from this FP -->
<layer>microprofile-graphql</layer>
<layer>micrometer</layer>
<layer>microprofile-telemetry</layer>
</layers>
</config>
</configurations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables combine.children="append">
<jboss.install.dir>${project.build.directory}/wildfly-test</jboss.install.dir>
<server.jvm.args>-Djboss.bind.address=${node0} -Djboss.bind.address.management=${node0} -Djboss.bind.address.unsecure=${node0}</server.jvm.args>
<node0>${node0}</node0>
</systemPropertyVariables>
</configuration>
</plugin>

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>

</project>
Loading

0 comments on commit d60bbf8

Please sign in to comment.