so I have this class in kotlin:
@Component
class UserResolver @Autowired
constructor(private val userService: UserService): GraphQLMutationResolver, GraphQLQueryResolver {
fun createUser(user: User): User {
userService.save(user)
return user
}
fun users(): Page<User> {
val pageable = QPageRequest(0, 10)
return userService.all(pageable)
}
}
I want the method users to return Page object, I am completely new to graphql. I tried something like this:
type Page {
number: Int
size: Int
numberOfElements: Int
content: []
hasContent: Boolean
isFirst: Boolean
isLast: Boolean
hasNext: Boolean
hasPrevoius: Boolean
totalPages: Int
totalElements: Float
}
but my spring boot app is failing to start, I have no ideas what my schema definition for this class https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Page.html should look like. Does anyone have an idea?
Edit: error is:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.TypeClassMatcher$RawClassRequiredForGraphQLMappingException: Type org.springframework.data.domain.Page<sk.matusko.wixit.common.dao.User > cannot be mapped to a GraphQL type! Since GraphQL-Java deals with erased types at runtime, only non-parameterized classes can represent a GraphQL type. This allows for reverse-lookup by java class in interfaces and union types.
The handling of generics has changed a bit with GraphQL Java Tools in the last few months. What version are you using? I just tried it with version 5.4.1 and it seemed to work, though with 5.2.4 it didn't. I wonder if the fix to this issue might be related.
In case it helps, here's the test stuff I used:
Firstly, I declared a class like this: class Page<T>(val number: Int, val size: Int)
Secondly, in the resolver I had fun users() = Page<User>(...)
Thirdly, in the GraphQL schema file I had this
type Page {
number: Int
size: Int!
}
type Query {
users: Page
}
The limitation with the above is that you have a single type in the GraphQL schema, simply called Page
. But presumably you want something specific about User
objects? Is this meant to be in the content
property in your example? If so, I think you need to declare a separate type in the GraphQL schema for every type that you might pass into the generic type parameter of Page
, e.g. UserPage
, CustomerPage
, etc. Then in the code each of those needs to be mapped to the correct Kotlin class, e.g. Page<User>
, Page<Customer>
. I don't know of a way to do that in code without having a concrete implementation of each of the generics (hopefully if it's possible someone else can explain how to do this). The GraphQL types names are married up to the Kotlin class names by default if they have the same name, or using an explicitly provided mapping when building the Schema
using SchemaParser.newParser().dictionary(...
So if you're happy creating concrete subclasses of the generic class for each type you supply to it, you can do something like this:
open class Page<T>(val number: Int, val size: Int, val content: List<T>)
class UserPage(number: Int, size: Int, content: List<User>): Page<User>(number, size, content)
fun users(): UserPage {...
And in the GraphQL schema you'd have this:
type UserPage {
number: Int!
size: Int!
content: [User]!
}
type Query {
users: UserPage
}