Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

some small fixes (documentation/code) #2205

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -61,25 +62,23 @@ private boolean hasOperations(GroupContainer namespace) {
}

public void add(Collection<String> names, String description, Operation operation) {
if (!names.isEmpty()) {
ArrayDeque<String> queue = new ArrayDeque<>(names);
String name = queue.poll();
add(name, description, queue, operation);
} else {
throw new RuntimeException("Namespaces can't be empty");
if (names.isEmpty()) {
throw new IllegalArgumentException("Namespaces cannot be empty");
}
Deque<String> queue = new ArrayDeque<>(names);
String name = queue.poll();
add(name, description, queue, operation);
}

private void add(String name, String description, ArrayDeque<String> queue, Operation operation) {
private void add(String name, String description, Deque<String> queue, Operation operation) {
this.name = name;

if (!queue.isEmpty()) {
if (queue.isEmpty()) {
this.description = description;
this.operations.add(operation);
} else {
String key = queue.poll();
GroupContainer groupContainer = container.computeIfAbsent(key, s -> new GroupContainer());
groupContainer.add(key, description, queue, operation);
} else {
this.description = description;
this.operations.add(operation);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -61,16 +62,15 @@ private boolean hasOperations(NamespaceContainer namespace) {

public void add(Collection<String> names, String description, Operation operation) {
if (names.isEmpty()) {
throw new RuntimeException("Namespaces can't be empty");
throw new IllegalArgumentException("Namespaces cannot be empty");
}
ArrayDeque<String> queue = new ArrayDeque<>(names);
Deque<String> queue = new ArrayDeque<>(names);
String name = queue.poll();
add(name, description, queue, operation);
}

private void add(String name, String description, ArrayDeque<String> queue, Operation operation) {
private void add(String name, String description, Deque<String> queue, Operation operation) {
this.name = name;

if (queue.isEmpty()) {
this.description = description;
this.operations.add(operation);
Expand Down
24 changes: 12 additions & 12 deletions docs/federation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Federation

Support for [GraphQL Federation](https://www.apollographql.com/docs/federation) is enabled by default. If you add one of the federation annotations, the corresponding directives will be declared to your schema and the additional Federation queries will be added automatically. You can also disable Federation completely by setting the `smallrye.graphql.federation.enabled` config key to `false`.
Support for [GraphQL Federation](https://www.apollographql.com/docs/federation) is enabled by default. If you add one of the federation annotations, the corresponding directives will be declared in your schema, and the additional Federation queries will be added automatically. You can also disable Federation completely by setting the `smallrye.graphql.federation.enabled` config key to `false`.

You can add the Federation directives by using the equivalent Java annotation, e.g. to extend a `Product` entity with a `price` field, you can write a class:

Expand Down Expand Up @@ -37,7 +37,7 @@ import org.eclipse.microprofile.graphql.Query;
public class Prices {
@Query
public Product product(@Id String id) {
return ...
return [...]
}
}
```
Expand All @@ -62,9 +62,9 @@ type Query {
If you can resolve, e.g., the product with different types of ids, you can add multiple `@Key` annotations.
## Federation Batch Resolver

For better performance, there is the option to use batch resolvers with federation. This is not activated on default. It can be activated by setting `smallrye.graphql.federation.batchResolving.enabled` config key to `true`.
For better performance, there is the option to use batch resolvers with federation. This is not activated by default. It can be activated by setting `smallrye.graphql.federation.batchResolving.enabled` config key to `true`.

It is not needed to provide batch resolvers for all entities, if there is no batch resolver, a non-batch resolver is used.
It is unnecessary to provide batch resolvers for all entities, if there is no batch resolver, a non-batch resolver is used.

```java
package org.example.price;
Expand All @@ -77,16 +77,16 @@ import org.eclipse.microprofile.graphql.Query;
public class Prices {
@Query
public List<Product> product(@Id List<String> id) {
return ...
return [...]
}
}
```

It is crucial that the sequence of argument list matches with the order of result list. Currently, the name of the Argument `id` must match with the property name in the type.
It is crucial that the sequence of the argument list matches the order of the result list. Currently, the name of the Argument `id` must match with the property name in the type.

## Federation Reference Resolver

In federation you also may want extend external type by some fields, without publishing queries into schema. You can do it using @Resolver
In federation, you may also want to extend the external type by some fields without publishing queries into the schema. You can do it by using the annotation `@Resolver`.

```java
@Extends
Expand Down Expand Up @@ -133,7 +133,7 @@ public class Api {
}
```

Will be generated next schema
Generates the following schema:
```
type Product @extends @key(fields : "upc") {
anotherWeight: Int! @requires(fields : "price")
Expand All @@ -150,7 +150,7 @@ type Query {
}
```

These methods will only be available to the federation router, which send next request
These methods will only be available to the federation router, which may send requests like the following examples:
```
// request 1
query {
Expand Down Expand Up @@ -197,9 +197,9 @@ query {
}
```

Unfortunately, you will have to make separate methods with different `@External` parameters.
Unfortunately, you must make separate methods with different `@External` parameters.

It is not currently possible to combine them into one separate type.

You also can using @Query (if you want add queries into schema) or @Resolver (requests 0 and 1).
And if it was request `_entities` - @Resolvers methods are checked first (they have higher priority).
You also can use `@Query` (if you want to add queries into the schema) or `@Resolver` (requests 0 and 1).
And if it was requested, `_entities` - `@Resolvers` methods are checked first (they have higher priority).
36 changes: 17 additions & 19 deletions docs/namespaces-on-server-side.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> Using approaches to form namespaces in the schema can be useful for large APIs. There are several ways to do this.
> However, read the documentation carefully, especially the limitations and possible problems.

> [NOTE] You can only use one of the annotations - @Name or @Namespace over the GraphQLApi classes.
> [NOTE] You can only use one of the annotations - `@Name` or `@Namespace` over the GraphQLApi classes.

## Using @Namespace annotation

Expand Down Expand Up @@ -34,7 +34,7 @@ public class AdminApi {
}
```

Will be generated schema
Will generate schema
```
"Query root"
type Query {
Expand Down Expand Up @@ -63,7 +63,7 @@ type User {
}
```

And you will can send such request
And you can send GraphQL request like:
```
query {
admin {
Expand All @@ -79,7 +79,7 @@ query {
## Using @Name annotation (deprecated)
> [NOTE] This feature may be removed in the future.

Does the same thing as @Namespace, the only difference is that there can only be one nesting level.
It does the same thing as `@Namespace`, the only difference is that there can only be one nesting level.
```java
@GraphQLApi
@Name("users")
Expand All @@ -103,23 +103,22 @@ query {
```

## Problems
While dividing APIs into namespaces may seem convenient, it has several issues that are important to be aware of.
While dividing APIs into namespaces may seem convenient, several issues are important to be aware of.

#### Mutations
Be careful when working with mutations on client.
This violates the Graphql specification, since mutations in this form can be executed in parallel.
On the client side, be careful when working with mutations.
This violates the GraphQL specification since mutations in this form can be executed in parallel.
Read more here about namespaces [Namespacing by Separation of Concerns](https://www.apollographql.com/docs/technotes/TN0012-namespacing-by-separation-of-concern/).
This article describes how you can work with namespaces, what problems you may encounter, and how to solve them.
This article describes how to work with namespaces, what problems you may encounter, and how to solve them.

What does Graphql say about this - ["GraphQL" Nested Mutations](https://benjie.dev/graphql/nested-mutations)
What does GraphQL say about this - ["GraphQL" Nested Mutations](https://benjie.dev/graphql/nested-mutations)

In summary, you can use nested mutations, but with some overhead on client. Be careful with mutations.
In summary, you can use nested mutations, but with some overhead on the client side. Be careful with mutations.

#### Subscriptions
Graphql does not allow creating subscriptions inside namespaces.
Or rather, you can create them, generated schema will be valid, but the subscription will not be resolved.
As example, if you try to run such a subscription request, you will get an error. This is the behavior of `graphql-java`.

GraphQL does not guarantee subscription resolution within namespaces.
While you can define subscriptions in namespaces, the generated schema will be valid, but the subscription will not be resolved.
For example, if you try to run such a subscription request, you will get an error. This is the behavior of `graphql-java`.
```java
@GraphQLApi
@Namepace("resource")
Expand All @@ -141,9 +140,8 @@ subscription {
}
```

There is currently no way around this problem.
You must move subscriptions into a separate class that is not placed in a namespace.

There is currently no way around this problem.
You must move subscriptions into a separate `@GraphQLApi` class that does not declare any namespace.
> [NOTE]
> Be very careful when designing API with namespace.
> And take into account all the features of working with mutations and subscriptions.
> Be very careful when designing API with namespace.
> And be aware of all the potential consequences when working with mutations and subscriptions.
36 changes: 17 additions & 19 deletions docs/typesafe-client-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ support, you need to instantiate the API interface by using the builder:
SuperHeroesApi api = TypesafeGraphQLClientBuilder.newBuilder().build(SuperHeroesApi.class);
```

The basic idea of the Java code-first approach is that you start by
The basic idea of the Java code-first approach is to start by
writing the DTOs and query/mutation methods as you need them in your
client. This ensures that you don’t request fields that you don’t need;
the thinking is inspired by [Consumer Driven
Contracts](https://martinfowler.com/articles/consumerDrivenContracts.html).


If the server uses names different from yours, you can simply use
annotations to do a mapping:
If the server uses names different from yours, you can use annotations to do a mapping:


### Name Mapping / Aliases

Expand Down Expand Up @@ -101,7 +101,7 @@ test system. Simply use [MicroProfile
Config](https://download.eclipse.org/microprofile/microprofile-config-1.4/microprofile-config-spec.html)
to set the endpoint; similar to the [MicroProfile Rest
Client](https://download.eclipse.org/microprofile/microprofile-rest-client-1.4.1/microprofile-rest-client-1.4.1.html),
the key for the endpoint is the fully qualified name of the api
the key for the endpoint is the fully qualified name of the API
interface, plus `/mp-graphql/url`, e.g.:

``` properties
Expand Down Expand Up @@ -177,7 +177,7 @@ query {
}
```

On client side you can create next code
On the client side, you can declare the following code:
```java
public record PermissionType(Long id) {
}
Expand All @@ -200,19 +200,18 @@ public interface ApiClient {

## Namespaces

There are several ways to work with namespaces in a type-safe client
1. Using @Namespace
2. Using @Name (deprecated)
There are several ways to work with namespaces in a type-safe client:
1. Using `@Namespace`
2. Using `@Name` (deprecated)

> [NOTE] You can only use one of the annotations - @Name or @Namespace over the GraphQLApi classes.
> [NOTE] You can only use one of the annotations - `@Name` or `@Namespace` on a `GraphClientQLApi` interface.

### Using @Namespace annotation

The annotation accepts an array of strings containing the nesting of the namespace.
This method allows you to create any nesting of namespaces.
You can use any nesting and also combine different levels.
The `@Namespace` annotation accepts an array of strings specifying the nesting levels of the namespace.
This flexible approach allows you to create namespaces with any desired level of nesting, combining different levels as needed.

If remote graphql api has next schema
If the remote GraphQL API has the following schema:
```
"Query root"
type Query {
Expand All @@ -229,11 +228,11 @@ type AdminUsersQuery {

type User {
id: BigInteger
...
[...]
}
```

You can create next interface
While declaring the following interface:
```java
@Namespace({"admin", "users"})
@GraphQLClientApi
Expand All @@ -242,7 +241,7 @@ public interface UsersClient {
}
```

Here will be generated next query
Its outcome will be the following GraphQL query:
```
query AminUsersFindAll {
admin {
Expand All @@ -258,13 +257,12 @@ query AminUsersFindAll {
### Using @Name (deprecated)
> [NOTE] This feature may be removed in the future.

Does the same thing as @Namespace, the only difference is that there can only be one nesting level.

The `@Name` annotation functions similarly to `@Namespace`, but it is limited to a single nesting level.
```
query {
users {
findAll {
....
...
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ private void generateGraphQLSchema() {
if (Config.get().isFederationEnabled()) {
log.enableFederation();

// hack! For schema build success if queries are empty.
// It will be overrides in Federation transformation
// Hack: Prevents schema build errors when queries are empty.
// Will be overridden during the Federation transformation.
addDummySdlQuery(schemaBuilder, queryRootType);

// Build reference resolvers type, without adding to schema (just for federation)
Expand Down
Loading