diff --git a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/GroupContainer.java b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/GroupContainer.java index 95d4b8f7b..80b6c78ad 100644 --- a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/GroupContainer.java +++ b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/GroupContainer.java @@ -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; @@ -61,25 +62,23 @@ private boolean hasOperations(GroupContainer namespace) { } public void add(Collection names, String description, Operation operation) { - if (!names.isEmpty()) { - ArrayDeque 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 queue = new ArrayDeque<>(names); + String name = queue.poll(); + add(name, description, queue, operation); } - private void add(String name, String description, ArrayDeque queue, Operation operation) { + private void add(String name, String description, Deque 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); } } diff --git a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/NamespaceContainer.java b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/NamespaceContainer.java index 2552a0a6e..01e2bb864 100644 --- a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/NamespaceContainer.java +++ b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/NamespaceContainer.java @@ -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; @@ -61,16 +62,15 @@ private boolean hasOperations(NamespaceContainer namespace) { public void add(Collection names, String description, Operation operation) { if (names.isEmpty()) { - throw new RuntimeException("Namespaces can't be empty"); + throw new IllegalArgumentException("Namespaces cannot be empty"); } - ArrayDeque queue = new ArrayDeque<>(names); + Deque queue = new ArrayDeque<>(names); String name = queue.poll(); add(name, description, queue, operation); } - private void add(String name, String description, ArrayDeque queue, Operation operation) { + private void add(String name, String description, Deque queue, Operation operation) { this.name = name; - if (queue.isEmpty()) { this.description = description; this.operations.add(operation); diff --git a/docs/federation.md b/docs/federation.md index 3ed10fcf7..64c68960f 100644 --- a/docs/federation.md +++ b/docs/federation.md @@ -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: @@ -37,7 +37,7 @@ import org.eclipse.microprofile.graphql.Query; public class Prices { @Query public Product product(@Id String id) { - return ... + return [...] } } ``` @@ -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; @@ -77,16 +77,16 @@ import org.eclipse.microprofile.graphql.Query; public class Prices { @Query public List product(@Id List 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 @@ -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") @@ -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 { @@ -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). \ No newline at end of file diff --git a/docs/namespaces-on-server-side.md b/docs/namespaces-on-server-side.md index ba76a0bec..741b148a1 100644 --- a/docs/namespaces-on-server-side.md +++ b/docs/namespaces-on-server-side.md @@ -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 @@ -34,7 +34,7 @@ public class AdminApi { } ``` -Will be generated schema +Will generate schema ``` "Query root" type Query { @@ -63,7 +63,7 @@ type User { } ``` -And you will can send such request +And you can send GraphQL request like: ``` query { admin { @@ -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") @@ -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") @@ -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. \ No newline at end of file diff --git a/docs/typesafe-client-usage.md b/docs/typesafe-client-usage.md index 8baacfb01..972a2afb6 100644 --- a/docs/typesafe-client-usage.md +++ b/docs/typesafe-client-usage.md @@ -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 @@ -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 @@ -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) { } @@ -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 { @@ -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 @@ -242,7 +241,7 @@ public interface UsersClient { } ``` -Here will be generated next query +Its outcome will be the following GraphQL query: ``` query AminUsersFindAll { admin { @@ -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 { - .... + ... } } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java index 73045f716..7bd2cd619 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java @@ -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)