Проблема рекурсии GraphQL Java Annotations

Я пытаюсь создать рекурсивную схему с использованием аннотаций GraphQL Java, но выдает исключение.

import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.ThreadLocalRandom;

import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.annotations.GraphQLAnnotations;
import graphql.annotations.GraphQLDataFetcher;
import graphql.annotations.GraphQLDescription;
import graphql.annotations.GraphQLField;
import graphql.annotations.GraphQLName;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;

import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;

public class RecursiveSchemaTest {

  @GraphQLDescription("TestObject object")
  @GraphQLName("TestObject")
  public static class TestObject {

    @GraphQLField
    private Integer id;

    @GraphQLField
    @GraphQLDataFetcher(TestObjectDataFetcher.class)
    private TestObject child;

    public TestObject(Integer id) {
      this.id = id;
    }

    public Integer getId() {
      return id;
    }

    public void setId(Integer id) {
      this.id = id;
    }

    public TestObject getChild() {
      return child;
    }

    public void setChild(TestObject child) {
      this.child = child;
    }
  }

  public static class TestObjectDataFetcher implements DataFetcher<TestObject> {

    @Override
    public TestObject get(DataFetchingEnvironment environment) {
      return new TestObject(ThreadLocalRandom.current().nextInt());
    }
  }

  @Test
  public void test() {
    GraphQLObjectType graphQLObjectType = GraphQLAnnotations.object(TestObject.class);
    GraphQLObjectType rootQuery = GraphQLObjectType.newObject().name("data").field(
        newFieldDefinition().name(graphQLObjectType.getName()).type(graphQLObjectType)
            .dataFetcher(new TestObjectDataFetcher()).build()).build();

    GraphQLSchema schema = GraphQLSchema.newSchema().query(rootQuery).build();
    GraphQL graphQL = GraphQL.newGraphQL(schema).build();

    ExecutionResult result = graphQL.execute("{ TestObject { id, child { id , child { id }}");
    Assert.assertFalse(result.getErrors() != null && !result.getErrors().isEmpty());
    Assert.assertNotNull(result.getData());
  }
}

Анализ класса проходит нормально, но создание схемы вызывает следующее исключение (эта строка: GraphQLSchema schema = GraphQLSchema.newSchema().query(rootQuery).build();):

graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'TestObject' from being a 'GraphQLObjectTypeWrapper' to a 'GraphQLObjectTypeWrapper'

    at graphql.schema.SchemaUtil.assertTypeUniqueness(SchemaUtil.java:86)
    at graphql.schema.SchemaUtil.collectTypesForObjects(SchemaUtil.java:122)
    at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:56)
    at graphql.schema.SchemaUtil.collectTypesForObjects(SchemaUtil.java:128)
    at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:56)
    at graphql.schema.SchemaUtil.collectTypesForObjects(SchemaUtil.java:128)
    at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:56)
    at graphql.schema.SchemaUtil.allTypes(SchemaUtil.java:153)
    at graphql.schema.GraphQLSchema.<init>(GraphQLSchema.java:42)
    at graphql.schema.GraphQLSchema$Builder.build(GraphQLSchema.java:130)
    at graphql.schema.GraphQLSchema$Builder.build(GraphQLSchema.java:125)
    at RecursiveSchemaTest.test(RecursiveSchemaTest.java:74)

Любые идеи, почему схема создана неправильно? Я использую последние версии аннотаций graphql-java (3.0.0) и graphql-java-annotations (0.14.0).


person ifodor    schedule 13.06.2017    source источник
comment
Я считаю, что это ошибка с аннотацией graphql-java-, над которой работают (член команды упомянул, что скоро выйдет релиз). Предыдущая версия graphql-java позволяла дублировать имена типов, но начиная с 3.0.0 это ошибка, и библиотека аннотаций еще не догнала. Кстати, посмотрите мою библиотеку, graphql-spqr, она позволяет еще более автоматизировать создание схемы.   -  person kaqqao    schedule 15.06.2017
comment
Ах, это облом. Есть ли проблема, зарегистрированная для этого на Github? Не нашел, могу создать, если нужно. Graphql-spqr выглядит довольно круто, возьму его на пробу :)   -  person ifodor    schedule 15.06.2017
comment
Превратил мой комментарий в ответ и связал проблему с отслеживанием обновления до 3.0.0, которое также решает вашу проблему. Что касается graphql-spqr, загляните в комнату Gitter, если вам нужна помощь, по крайней мере до тех пор, пока Я заканчиваю писать документы (сейчас работаю над этим).   -  person kaqqao    schedule 15.06.2017
comment
Добавил пример в мой ответ   -  person kaqqao    schedule 16.06.2017


Ответы (1)


Я считаю, что это ошибка с аннотацией graphql-java, которая уже была закрыто. Предыдущая версия graphql-java позволяла дублировать имена типов, но начиная с 3.0.0 это ошибка, и библиотека аннотаций еще не догнала.

Исправление должно быть в ближайшем релизе...

Кстати, посмотрите мою библиотеку, graphql-spqr, она позволяет еще более автоматизировать создание схемы и с легкостью покроет ваш вариант использования:

public static class TestObject {
    private Integer id;

    public TestObject(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}

public static class TestObjectService {

    @GraphQLQuery(name = "TestObject")
    public TestObject getRoot() { //no GraphQL-specific classes mentioned
        return getRandom();
    }

    @GraphQLQuery(name = "child")
    public TestObject getChild(@GraphQLContext TestObject parent) {
        return getRandom();
    }

    private TestObject getRandom() {
        return new TestObject(ThreadLocalRandom.current().nextInt());
    }
}

@Test
public void test() {
    GraphQLSchema schema = new GraphQLSchemaGenerator()
            .withOperationsFromSingleton(new TestObjectService())
            .generate(); //that's all :)
    GraphQL graphQL = GraphQL.newGraphQL(schema).build();

    ExecutionResult result = graphQL.execute("{ TestObject { id, child { id , child { id }}}}"); //your query has a syntax error
    assertFalse(result.getErrors() != null && !result.getErrors().isEmpty());
    assertNotNull(result.getData());
}

Обратите внимание, что я удалил свойство child из TestObject, так как оно на самом деле не использовалось (поскольку оно было заменено другим сборщиком). Тем не менее, если вы оставите его в покое, разницы не будет — пользовательский сборщик (вложенный через @GraphQLContext) все равно переопределит его. Идея, лежащая в основе @GraphQLContext, состоит в том, чтобы разрешить вложенные запросы без необходимости впихивать логику в модель или даже когда-либо касаться объектов модели.

Поля также можно аннотировать, если вы хотите переименовать их или добавить описания, например.

@GraphQLQuery(name = "child", description = "The child object")
public TestObject getChild() {
    return child;
}
person kaqqao    schedule 15.06.2017
comment
Проблема, на которую вы ссылаетесь, содержит PR, который устраняет исходную проблему. Его еще нет в центре maven, но он работает локально! Спасибо - person ifodor; 21.06.2017