Как проверить запрошенные поля GraphQL в Java с фрагментами? - PullRequest
1 голос
/ 16 апреля 2020

У меня запрос к GraphQL, похожий на этот:

query {
  getPosts {    
      ...PostFragment
  }
}

fragment SpecificPostFragment on SpecificPost {
  owner {
    id
    name
  }
}

fragment PostFragment on Post {
  id
  object
  ... on SpecificPost {
    ...SpecificPostFragment 
  }
}

Я пытаюсь узнать:

  1. запрашивается ли объект поля
  2. владелец поля запрашивается

Я пытаюсь применить то, что написано здесь: https://www.graphql-java.com/documentation/v11/fieldselection/

Но dataFetchingEnvironment.getSelectionSet().contains("XXX"), похоже, плохо работает с фрагментами. Как это сделать?

1 Ответ

0 голосов
/ 18 апреля 2020

Я не нашел никакого встроенного решения, поэтому написал свое. Вот мой код

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import graphql.execution.MergedField;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import graphql.language.FragmentSpread;
import graphql.language.InlineFragment;
import graphql.language.SelectionSet;
import graphql.schema.DataFetchingEnvironment;

public class GraphQLUtil {
    private static class PathElement {
        private final String name;
        private final String typeName;

        public PathElement(String name, String typeName) {
            this.name = name;
            this.typeName = typeName;
        }

        public String getName() {
            return name;
        }

        public String getTypeName() {
            return typeName;
        }
    }

    public static boolean containsIncludingFragments(DataFetchingEnvironment env, String path) {
        Objects.requireNonNull(env, "The data fetching environment must not be null");
        Objects.requireNonNull(path, "The field path must not be null");

        List<PathElement> elts = Stream.of(path.split("/")).map(p -> {
            String pt = p.trim();
            if (pt.isEmpty()) {
                throw new IllegalArgumentException("Empty path element found");
            }
            int sepIdx = pt.indexOf(":");
            String name = pt;
            String typeName = null;
            if (sepIdx >= 0) {
                typeName = pt.substring(0, sepIdx);
                name = pt.substring(sepIdx + 1, pt.length());
            }
            return new PathElement(name, typeName);
        }).collect(Collectors.toList());

        if (elts.isEmpty()) {
            return false;
        }

        MergedField mf = env.getMergedField();
        return searchPathElement(env, elts, 0, mf.getSingleField().getSelectionSet(), null);
    }

    private static boolean searchPathElement(
            DataFetchingEnvironment env,
            List<PathElement> elts,
            int eltIndex,
            SelectionSet selectionSet,
            String selectionTypeName) {
        if (eltIndex >= elts.size()) {
            return true;
        }

        PathElement currentElt = elts.get(eltIndex);
        String currentName = currentElt.getName();
        String currentTypeName = currentElt.getTypeName();
        List<Field> fields = selectionSet.getSelectionsOfType(Field.class);
        boolean found = false;
        for (Field f : fields) {
            if (f.getName().equals(currentName) && (currentTypeName == null
                    || selectionTypeName == null
                    || currentTypeName.equals(selectionTypeName))) {
                found = searchPathElement(env, elts, eltIndex + 1, f.getSelectionSet(), null);
                if (found) {
                    return true;
                }
            }
        }

        List<FragmentSpread> fragments = selectionSet.getSelectionsOfType(FragmentSpread.class);
        for (FragmentSpread f : fragments) {
            FragmentDefinition fDef = env.getFragmentsByName().get(f.getName());
            found = searchPathElement(env, elts, eltIndex, fDef.getSelectionSet(), fDef.getTypeCondition().getName());
            if (found) {
                return true;
            }
        }

        List<InlineFragment> inlineFragments = selectionSet.getSelectionsOfType(InlineFragment.class);
        for (InlineFragment f : inlineFragments) {
            found = searchPathElement(env, elts, eltIndex, f.getSelectionSet(), f.getTypeCondition().getName());
            if (found) {
                return true;
            }
        }

        return false;
    }
}

И вы называете его так:

  DataFetchingEnvironment dataEnv = ... // If like me you use GraphQL SPQR, you can get it with io.leangen.graphql.execution.ResolutionEnvironment
  boolean t1= GraphQLUtil.containsIncludingFragments(dataEnv, "id");
  boolean t2 = GraphQLUtil.containsIncludingFragments(dataEnv, "owner/id");
  boolean t3 = GraphQLUtil.containsIncludingFragments(dataEnv, "SpecificPost:owner/id"); // You may give the type of the field, if in some inheritance scenario, it is ambiguous

Обратите внимание, что это решение не поддерживает подстановочные знаки (* или?). И я не проверял, если основной запрос содержит несколько записей (например, getPost + getPeople в одном запросе), но в этом случае это, вероятно, не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...