diff --git a/products-subgraph/build.gradle.kts b/products-subgraph/build.gradle.kts index 5940eee..dea1eee 100644 --- a/products-subgraph/build.gradle.kts +++ b/products-subgraph/build.gradle.kts @@ -1,7 +1,7 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { - id("org.springframework.boot") version "3.1.4" + id("org.springframework.boot") version "3.3.0-RC1" id("io.spring.dependency-management") version "1.1.3" java } @@ -11,6 +11,7 @@ version = "0.0.1-SNAPSHOT" repositories { mavenCentral() + maven("https://repo.spring.io/milestone") } val federation_jvm_version: String = project.property("federation-jvm.version").toString() diff --git a/products-subgraph/src/main/java/com/example/products/GraphQLConfiguration.java b/products-subgraph/src/main/java/com/example/products/GraphQLConfiguration.java deleted file mode 100644 index 8257d22..0000000 --- a/products-subgraph/src/main/java/com/example/products/GraphQLConfiguration.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.example.products; - -import com.apollographql.federation.graphqljava.Federation; -import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class GraphQLConfiguration { - - @Bean - public GraphQlSourceBuilderCustomizer federationTransform() { - return builder -> { - builder.schemaFactory((registry, wiring)-> - Federation.transform(registry, wiring) - .fetchEntities(env -> null) - .resolveEntityType(env -> null) - .build() - ); - }; - } -} diff --git a/products-subgraph/src/main/java/com/example/products/ProductsApplication.java b/products-subgraph/src/main/java/com/example/products/ProductsApplication.java index fb74631..41f9b69 100644 --- a/products-subgraph/src/main/java/com/example/products/ProductsApplication.java +++ b/products-subgraph/src/main/java/com/example/products/ProductsApplication.java @@ -2,6 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.graphql.data.federation.FederationSchemaFactory; @SpringBootApplication public class ProductsApplication { @@ -10,4 +13,14 @@ public static void main(String[] args) { SpringApplication.run(ProductsApplication.class, args); } + @Bean + public GraphQlSourceBuilderCustomizer customizer(FederationSchemaFactory factory) { + return builder -> builder.schemaFactory(factory::createGraphQLSchema); + } + + @Bean + FederationSchemaFactory federationSchemaFactory() { + return new FederationSchemaFactory(); + } + } diff --git a/products-subgraph/src/main/java/com/example/products/ProductsController.java b/products-subgraph/src/main/java/com/example/products/ProductsController.java index 2fea829..775c28d 100644 --- a/products-subgraph/src/main/java/com/example/products/ProductsController.java +++ b/products-subgraph/src/main/java/com/example/products/ProductsController.java @@ -1,34 +1,25 @@ package com.example.products; +import java.util.List; + import com.example.products.model.Product; +import com.example.products.model.ProductSource; + import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.stereotype.Controller; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; - @Controller public class ProductsController { - private final Map PRODUCTS = Stream.of( - new Product("1","Saturn V", "The Original Super Heavy-Lift Rocket!"), - new Product("2","Lunar Module"), - new Product("3","Space Shuttle"), - new Product("4","Falcon 9", "Reusable Medium-Lift Rocket"), - new Product("5","Dragon", "Reusable Medium-Lift Rocket"), - new Product("6","Starship", "Super Heavy-Lift Reusable Launch Vehicle") - ).collect(Collectors.toMap(Product::id, product -> product)); - @QueryMapping - public Product product(@Argument String id) { - return PRODUCTS.get(id); + public Product product(@Argument Long id) { + return ProductSource.getProduct(id); } @QueryMapping public List products() { - return PRODUCTS.values().stream().toList(); + return ProductSource.getProducts(); } + } diff --git a/products-subgraph/src/main/java/com/example/products/model/Product.java b/products-subgraph/src/main/java/com/example/products/model/Product.java index ed826ac..a5f5cf6 100644 --- a/products-subgraph/src/main/java/com/example/products/model/Product.java +++ b/products-subgraph/src/main/java/com/example/products/model/Product.java @@ -7,9 +7,9 @@ * description: String * } */ -public record Product(String id, String name, String description) { +public record Product(Long id, String name, String description) { - public Product(String id, String name) { + public Product(Long id, String name) { this(id, name, null); } } diff --git a/products-subgraph/src/main/java/com/example/products/model/ProductSource.java b/products-subgraph/src/main/java/com/example/products/model/ProductSource.java new file mode 100644 index 0000000..c1d621f --- /dev/null +++ b/products-subgraph/src/main/java/com/example/products/model/ProductSource.java @@ -0,0 +1,29 @@ +package com.example.products.model; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public final class ProductSource { + + private static final List productList = List.of( + new Product(1L, "Saturn V", "The Original Super Heavy-Lift Rocket!"), + new Product(2L, "Lunar Module"), + new Product(3L, "Space Shuttle"), + new Product(4L, "Falcon 9", "Reusable Medium-Lift Rocket"), + new Product(5L, "Dragon", "Reusable Medium-Lift Rocket"), + new Product(6L, "Starship", "Super Heavy-Lift Reusable Launch Vehicle") + ); + + private static final Map productMap = + productList.stream().collect(Collectors.toMap(Product::id, product -> product)); + + public static Product getProduct(Long id) { + return productMap.get(id); + } + + public static List getProducts() { + return productList; + } + +} diff --git a/products-subgraph/src/main/resources/application.yml b/products-subgraph/src/main/resources/application.yml index 52586ed..63f9dcf 100644 --- a/products-subgraph/src/main/resources/application.yml +++ b/products-subgraph/src/main/resources/application.yml @@ -2,3 +2,6 @@ spring: graphql: graphiql: enabled: true +logging: + level: + org.springframework.graphql: DEBUG diff --git a/products-subgraph/src/test/java/com/example/products/ProductsApplicationTest.java b/products-subgraph/src/test/java/com/example/products/ProductsApplicationTest.java index 3bd3830..e2c0b0a 100644 --- a/products-subgraph/src/test/java/com/example/products/ProductsApplicationTest.java +++ b/products-subgraph/src/test/java/com/example/products/ProductsApplicationTest.java @@ -33,6 +33,6 @@ query ProductById($productId: ID!) { .execute() .path("product") .entity(Product.class) - .isEqualTo(new Product("1","Saturn V", "The Original Super Heavy-Lift Rocket!")); + .isEqualTo(new Product(1L,"Saturn V", "The Original Super Heavy-Lift Rocket!")); } } diff --git a/reviews-subgraph/build.gradle.kts b/reviews-subgraph/build.gradle.kts index fca26ce..a6fdfdd 100644 --- a/reviews-subgraph/build.gradle.kts +++ b/reviews-subgraph/build.gradle.kts @@ -1,7 +1,7 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { - id("org.springframework.boot") version "3.1.4" + id("org.springframework.boot") version "3.3.0-RC1" id("io.spring.dependency-management") version "1.1.3" java } @@ -11,6 +11,7 @@ version = "0.0.1-SNAPSHOT" repositories { mavenCentral() + maven("https://repo.spring.io/milestone") } val federation_jvm_version: String = project.property("federation-jvm.version").toString() diff --git a/reviews-subgraph/src/main/java/com/example/reviews/GraphQLConfiguration.java b/reviews-subgraph/src/main/java/com/example/reviews/GraphQLConfiguration.java deleted file mode 100644 index c6ec3e2..0000000 --- a/reviews-subgraph/src/main/java/com/example/reviews/GraphQLConfiguration.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.example.reviews; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import com.apollographql.federation.graphqljava.Federation; -import com.apollographql.federation.graphqljava._Entity; -import com.example.reviews.model.Product; -import graphql.schema.DataFetcher; - -import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.graphql.execution.ClassNameTypeResolver; - -import static com.example.reviews.model.Product.PRODUCT_TYPE; - -@Configuration -public class GraphQLConfiguration { - - @Bean - public GraphQlSourceBuilderCustomizer federationTransform() { - DataFetcher entityDataFetcher = env -> { - List> representations = env.getArgument(_Entity.argumentName); - return representations.stream() - .map(representation -> { - if (PRODUCT_TYPE.equals(representation.get("__typename"))) { - return new Product((String)representation.get("id")); - } - return null; - }) - .collect(Collectors.toList()); - }; - - return builder -> - builder.schemaFactory((registry, wiring)-> - Federation.transform(registry, wiring) - .fetchEntities(entityDataFetcher) - .resolveEntityType(new ClassNameTypeResolver()) - .build() - ); - } -} diff --git a/reviews-subgraph/src/main/java/com/example/reviews/ReviewsApplication.java b/reviews-subgraph/src/main/java/com/example/reviews/ReviewsApplication.java index 6bd8ca9..86b5650 100644 --- a/reviews-subgraph/src/main/java/com/example/reviews/ReviewsApplication.java +++ b/reviews-subgraph/src/main/java/com/example/reviews/ReviewsApplication.java @@ -2,6 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.graphql.data.federation.FederationSchemaFactory; @SpringBootApplication public class ReviewsApplication { @@ -10,4 +13,14 @@ public static void main(String[] args) { SpringApplication.run(ReviewsApplication.class, args); } + @Bean + public GraphQlSourceBuilderCustomizer customizer(FederationSchemaFactory factory) { + return builder -> builder.schemaFactory(factory::createGraphQLSchema); + } + + @Bean + FederationSchemaFactory federationSchemaFactory() { + return new FederationSchemaFactory(); + } + } diff --git a/reviews-subgraph/src/main/java/com/example/reviews/ReviewsController.java b/reviews-subgraph/src/main/java/com/example/reviews/ReviewsController.java index b3cfa31..ea30e50 100644 --- a/reviews-subgraph/src/main/java/com/example/reviews/ReviewsController.java +++ b/reviews-subgraph/src/main/java/com/example/reviews/ReviewsController.java @@ -1,26 +1,27 @@ package com.example.reviews; -import com.example.reviews.model.Review; +import java.util.List; + import com.example.reviews.model.Product; +import com.example.reviews.model.Review; +import com.example.reviews.model.ReviewSource; + +import org.springframework.graphql.data.federation.EntityMapping; +import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.stereotype.Controller; -import java.util.Collections; -import java.util.List; -import java.util.Map; - @Controller public class ReviewsController { - private final Map> REVIEWS = Map.of( - "2", List.of(new Review("1020", "Very cramped :( Do not recommend.", 2), new Review("1021", "Got me to the Moon!", 4)), - "3", List.of(new Review("1030", 3)), - "4", List.of(new Review("1040", 5), new Review("1041", "Reusable!", 5), new Review("1042", 5)), - "5", List.of(new Review("1050", "Amazing! Would Fly Again!", 5), new Review("1051", 5)) - ); + @EntityMapping + public Product product(@Argument Long id) { + return new Product(id); + } @SchemaMapping - public List reviews(Product show) { - return REVIEWS.getOrDefault(show.id(), Collections.emptyList()); + public List reviews(Product product) { + return ReviewSource.getReviews(product); } + } diff --git a/reviews-subgraph/src/main/java/com/example/reviews/model/Product.java b/reviews-subgraph/src/main/java/com/example/reviews/model/Product.java index 6f6225f..c94ad85 100644 --- a/reviews-subgraph/src/main/java/com/example/reviews/model/Product.java +++ b/reviews-subgraph/src/main/java/com/example/reviews/model/Product.java @@ -1,5 +1,5 @@ package com.example.reviews.model; -public record Product(String id) { +public record Product(Long id) { public static final String PRODUCT_TYPE = "Product"; } diff --git a/reviews-subgraph/src/main/java/com/example/reviews/model/ReviewSource.java b/reviews-subgraph/src/main/java/com/example/reviews/model/ReviewSource.java new file mode 100644 index 0000000..82c0848 --- /dev/null +++ b/reviews-subgraph/src/main/java/com/example/reviews/model/ReviewSource.java @@ -0,0 +1,20 @@ +package com.example.reviews.model; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public final class ReviewSource { + + private static final Map> reviewMap = Map.of( + 2L, List.of(new Review("1020", "Very cramped :( Do not recommend.", 2), new Review("1021", "Got me to the Moon!", 4)), + 3L, List.of(new Review("1030", 3)), + 4L, List.of(new Review("1040", 5), new Review("1041", "Reusable!", 5), new Review("1042", 5)), + 5L, List.of(new Review("1050", "Amazing! Would Fly Again!", 5), new Review("1051", 5)) + ); + + public static List getReviews(Product product) { + return reviewMap.getOrDefault(product.id(), Collections.emptyList()); + } + +} diff --git a/reviews-subgraph/src/main/resources/application.yml b/reviews-subgraph/src/main/resources/application.yml index f08157a..49d3cc0 100644 --- a/reviews-subgraph/src/main/resources/application.yml +++ b/reviews-subgraph/src/main/resources/application.yml @@ -5,4 +5,9 @@ spring: graphql: graphiql: enabled: true - + schema: + printer: + enabled: true +logging: + level: + org.springframework.graphql: DEBUG diff --git a/settings.gradle.kts b/settings.gradle.kts index 9f4143d..ed2fa28 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,11 @@ +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + maven("https://repo.spring.io/milestone") + } +} + rootProject.name = "federation-jvm-spring-example" include(":products-subgraph")