diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java index 5014301e..c51ccd25 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java @@ -65,36 +65,42 @@ final class EntitiesDataFetcher implements DataFetcher>> get(DataFetchingEnvironment environment) { - List> representations = environment.getArgument(_Entity.argumentName); + public Mono>> get(DataFetchingEnvironment env) { + List> representations = env.getArgument(_Entity.argumentName); + if (representations == null) { + return Mono.error(new RepresentationException( + Collections.emptyMap(), "Missing \"representations\" argument")); + } - Set batched = new HashSet<>(); - List> monoList = new ArrayList<>(); + Set batchedTypes = new HashSet<>(); + List> monoList = new ArrayList<>(); for (int index = 0; index < representations.size(); index++) { Map map = representations.get(index); - if (!(map.get("__typename") instanceof String typename)) { + if (!(map.get("__typename") instanceof String type)) { Exception ex = new RepresentationException(map, "Missing \"__typename\" argument"); - monoList.add(resolveException(ex, environment, null, index)); + monoList.add(resolveException(ex, env, null, index)); continue; } - EntityHandlerMethod handlerMethod = this.handlerMethods.get(typename); + EntityHandlerMethod handlerMethod = this.handlerMethods.get(type); if (handlerMethod == null) { Exception ex = new RepresentationException(map, "No entity fetcher"); - monoList.add(resolveException(ex, environment, null, index)); + monoList.add(resolveException(ex, env, null, index)); continue; } if (!handlerMethod.isBatchHandlerMethod()) { - monoList.add(invokeEntityMethod(environment, handlerMethod, map, index)); - } - else if (batched.contains(typename)) { - // zip needs a value, this will be replaced by batch results - monoList.add(Mono.just(Collections.emptyMap())); + monoList.add(invokeEntityMethod(env, handlerMethod, map, index)); } else { - EntityBatchDelegate batchDelegate = new EntityBatchDelegate(environment, handlerMethod, typename); - monoList.add(batchDelegate.invokeEntityBatchMethod()); - batched.add(typename); + if (!batchedTypes.contains(type)) { + EntityBatchDelegate delegate = new EntityBatchDelegate(env, representations, handlerMethod, type); + monoList.add(delegate.invokeEntityBatchMethod()); + batchedTypes.add(type); + } + else { + // Covered by batch invocation, but zip needs a value (to be replaced by batch results) + monoList.add(Mono.just(Collections.emptyMap())); + } } } return Mono.zip(monoList, Arrays::asList).map(EntitiesDataFetcher::toDataFetcherResult); @@ -108,7 +114,7 @@ final class EntitiesDataFetcher implements DataFetcher resolveException(ex, env, handlerMethod, index)); } - private Mono resolveException( + private Mono resolveException( Throwable ex, DataFetchingEnvironment env, @Nullable EntityHandlerMethod handlerMethod, int index) { Throwable theEx = (ex instanceof CompletionException) ? ex.getCause() : ex; @@ -117,8 +123,7 @@ final class EntitiesDataFetcher implements DataFetcher createDefaultError(theEx, theEnv))) - .cast(Object.class); + .switchIfEmpty(Mono.fromCallable(() -> createDefaultError(theEx, theEnv))); } private ErrorContainer createDefaultError(Throwable ex, DataFetchingEnvironment env) { @@ -154,28 +159,30 @@ final class EntitiesDataFetcher implements DataFetcher> representations = new ArrayList<>(); + private final List> filteredRepresentations = new ArrayList<>(); private final List indexes = new ArrayList<>(); @Nullable private List resultList; - EntityBatchDelegate(DataFetchingEnvironment env, EntityHandlerMethod handlerMethod, String typeName) { + EntityBatchDelegate( + DataFetchingEnvironment env, List> allRepresentations, + EntityHandlerMethod handlerMethod, String type) { + this.environment = env; this.handlerMethod = handlerMethod; - List> maps = env.getArgument(_Entity.argumentName); - for (int i = 0; i < maps.size(); i++) { - Map map = maps.get(i); - if (typeName.equals(map.get("__typename"))) { - this.representations.add(map); + for (int i = 0; i < allRepresentations.size(); i++) { + Map map = allRepresentations.get(i); + if (type.equals(map.get("__typename"))) { + this.filteredRepresentations.add(map); this.indexes.add(i); } } } Mono invokeEntityBatchMethod() { - return this.handlerMethod.getEntities(this.environment, this.representations) + return this.handlerMethod.getEntities(this.environment, this.filteredRepresentations) .mapNotNull((result) -> (((List) result).isEmpty()) ? null : result) .switchIfEmpty(Mono.defer(this::handleEmptyResult)) .onErrorResume(this::handleErrorResult) @@ -186,9 +193,9 @@ final class EntitiesDataFetcher implements DataFetcher handleEmptyResult() { - List> exceptions = new ArrayList<>(this.indexes.size()); + List> exceptions = new ArrayList<>(this.indexes.size()); for (int i = 0; i < this.indexes.size(); i++) { - Map map = this.representations.get(i); + Map map = this.filteredRepresentations.get(i); Exception ex = new RepresentationNotResolvedException(map, this.handlerMethod); exceptions.add(resolveException(ex, this.environment, this.handlerMethod, this.indexes.get(i))); } @@ -196,7 +203,7 @@ final class EntitiesDataFetcher implements DataFetcher> handleErrorResult(Throwable ex) { - List> list = new ArrayList<>(); + List> list = new ArrayList<>(); for (Integer index : this.indexes) { list.add(resolveException(ex, this.environment, this.handlerMethod, index)); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java index 2556bba9..578c21df 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntityHandlerMethod.java @@ -53,33 +53,23 @@ final class EntityHandlerMethod extends DataFetcherHandlerMethodSupport { Mono getEntity(DataFetchingEnvironment env, Map representation) { - Object[] args; - try { - env = EntityArgumentMethodArgumentResolver.wrap(env, representation); - args = getMethodArgumentValues(env); - } - catch (Throwable ex) { - return Mono.error(ex); - } - - return doInvoke(env, args); + env = EntityArgumentMethodArgumentResolver.wrap(env, representation); + return doInvoke(env); } - @SuppressWarnings("unchecked") Mono getEntities(DataFetchingEnvironment env, List> representations) { + env = EntityArgumentMethodArgumentResolver.wrap(env, representations); + return doInvoke(env); + } + + private Mono doInvoke(DataFetchingEnvironment env) { Object[] args; try { - env = EntityArgumentMethodArgumentResolver.wrap(env, representations); args = getMethodArgumentValues(env); } catch (Throwable ex) { return Mono.error(ex); } - - return doInvoke(env, args); - } - - private Mono doInvoke(DataFetchingEnvironment env, Object[] args) { Object result = doInvoke(env.getGraphQlContext(), args); return ReactiveAdapterRegistryHelper.toMono(result); }