Skip to content

Batched resolver with arguments #93

@picodotdev

Description

@picodotdev

I'm trying to use a batched resolver with arguments that implements pagination with cursors for the batchedComments property in the following graphql schema:

type Book {
    id: Long
    title: String
    author: Author
    isbn: String
    comments(after: String, limit: Long): CommentsConnection
    batchedComments(after: String, limit: Long): CommentsConnection
}

type Comment {
    id: Long
    text: String
}

type Author {
    id: Long
    name: String
}

input BookFilter {
    title: String
}

type CommentsConnection {
    edges: [CommentEdge]
    pageInfo: PageInfo
}

type CommentEdge {
    node: Comment
    cursor: String
}

type PageInfo {
    startCursor: String
    endCursor: String
    hasNextPage: Boolean
}

type Query {
    books: [Book]!
    findBooks(filter: BookFilter): [Book]!
    authors: [Author]!
    author(id: Long): Author!
}

type Mutation {
    addBook(title: String, author: Long): Book
}

schema {
    query: Query
    mutation: Mutation
}

This is my resolver:

public class BookResolver implements GraphQLResolver<Book> {

    private LibraryRepository libraryRespository;

    public BookResolver(LibraryRepository libraryRespository) {
        this.libraryRespository = libraryRespository;
    }

    public String getIsbn(Book book) {
        return UUID.randomUUID().toString();
    }

    public CommentsConnection getComments(Book book, String after, Long limit) {
        Long idAfter = null;
        Long limitPlusOne = null;

        if (after != null) {
            idAfter = CommentEdge.fromGlobalId(after);
        }
        if (limit != null && limit < Long.MAX_VALUE) {
            limitPlusOne = limit + 1;
        }

        List<Comment> commentsPlusOne = libraryRespository.findComments(book.getId(), idAfter, limitPlusOne);
        Stream<Comment> stream = commentsPlusOne.stream();
        if (limit != null) {
            stream = stream.limit(limit);
        }
        List<Comment> comments = stream.collect(Collectors.toList());

        Comment firstComment = (!comments.isEmpty()) ? comments.get(0) : null;
        Comment lastComment = (!comments.isEmpty()) ? comments.get(comments.size() - 1) : null;

        String startCursor = (firstComment != null) ? CommentEdge.toGlobalId(firstComment) : null;
        String endCursor = (lastComment != null) ? CommentEdge.toGlobalId(lastComment) : null;

        boolean hasNextPage = commentsPlusOne.size() > comments.size();

        return new CommentsConnection(comments, new PageInfo(startCursor, endCursor, hasNextPage));
    }

    @Batched
    public List<CommentsConnection> getBatchedComments(List<Book> books, List<String> after, List<Long> limit) {
        List<CommentsConnection> ccs = new ArrayList<>();
        for (int i = 0; i < books.size(); ++i) {
            CommentsConnection cc = getComments(books.get(i), null, null);
            ccs.add(cc);
        }
        return ccs;
    }
}

But when I do this request I get an exception:

$ curl -XPOST -H "Content-Type: application/json" -d '{"query": "query Books{books{title isbn batchedComments(limit:1){edges{node{text}}}}}"}' http://localhost:8080/library
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_NUMBER_INT token
 at [Source: N/A; line: -1, column: -1]
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1122) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1075) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:338) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:269) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26) ~[jackson-databind-2.8.10.jar:2.8.10]
	at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3600) ~[jackson-databind-2.8.10.jar:2.8.10]
	... 74 common frames omitted

Maybe I would expect the batched resolver signature was this instead:

@Batched
public List<CommentsConnection> getBatchedComments(List<Book> books, String after, Long limit)

But with this resolver signature on application start throws this exception:

Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (TypeName{name='String'}) with java type (class java.lang.String): Method was marked as @Batched but parameter was not a list!
	at com.coxautodev.graphql.tools.TypeClassMatcher.error(TypeClassMatcher.kt:22) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.TypeClassMatcher.stripBatchedType(TypeClassMatcher.kt:99) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:26) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:215) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueueItemForPotentialMatches(SchemaClassScanner.kt:206) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueue(SchemaClassScanner.kt:103) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:81) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:149) ~[graphql-java-tools-4.3.0.jar:na]
	at com.coxautodev.graphql.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:155) ~[graphql-java-tools-4.3.0.jar:na]
	at io.github.picodotdev.blogbitix.graphql.Main.graphQLServletRegistrationBean(Main.java:42) [main/:na]

The full source code with a spring boot example is here: https://github.com/picodotdev/blog-ejemplos/tree/master/GraphQL

How I can use a batched resolver with arguments? Is it possible?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions