Skip to content
This repository was archived by the owner on Apr 25, 2023. It is now read-only.

p-louis/graphql-pagination-plugin

Repository files navigation

Snapshot Release

GraphQL-pagination-plugin

Plugin for the Kotlin compiler to generate boilerplate code for graphql-pagination for use with kgraphql.

The generated pagination is implemented according to the graphQL-best practice.

Project setup

Gradle

Besides the compile dependency

build.gradle.kts

repositories {
    mavenCentral()
    maven { url = uri("https://maven.pkg.github.com/linked-planet/graphql-pagination-plugin") }
}

dependencies {
    implementation(group = "com.apurebase", name = "kgraphql", version = kGraphQLVersion)
    compile(group = "com.linked-planet.plugin", name = "graphql-plugin", version = graphQLPluginVersion) 
}

using this plugin will require letting the compiler know it is supposed to use it. To allow for this, we need to determine the location of its jar-file.

build.gradle.kts

fun classpathOf(dependency: String): File? {
    val regex = Regex(".*${dependency.replace(':', '-')}.*")
    return project.configurations.compile.get().firstOrNull { regex.matches(it.name) }
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = jvmTarget
    kotlinOptions.freeCompilerArgs = listOf(
        "-Xplugin=${classpathOf("graphql-plugin:$graphQLPluginVersion")}",
        "-P", "plugin:arrow.meta.plugin.compiler:generatedSrcOutputDir=${buildDir}"
    )
}

Usage

Usage of this is pretty simple, each data class to be paginated needs two things:

  • @Paginated annotation
  • a property annotated with @Identifier
  • a companion object (may also be empty)
import com.linkedplanet.plugin.graphqlplugin.*

@Paginated
data class Example(
    @Identifier val uniqueId: Int, 
    val someNumber: Int
    ) { companion object }

The Property chosen with @Identifier will influence the type signature of all functions that externalize the pagination (i.e. SQL-Queries).

Now you can extend your schema with the generated functionality:

Connection Properties

In memory pagination

KGraphQL.schema { 
    type<PageInfo>() // part of com.linkedplanet.plugin.graphqlplugin
    Example.registerTypes(this)
    type<SomeType>() {
        Example.connectionProperty(
            this,
            "exampleConnections", 
            { parent: SomeType -> ExampleProvider.getExamplesForParent(parent) }// :(SomeType)->List<Example>
        )
    }
    
    //...
}

Externalized pagination

KGraphQL.schema { 
    type<PageInfo>() // part of com.linkedplanet.plugin.graphqlplugin
    Example.registerTypes(this)
    type<SomeType>() {
        Example.connectionProperty(
            this,
            "exampleConnections",
            // (SomeType)->Int
            ExampleProvider::getTotalCount, 
            // (SomeType, Type of the Identifier-annotated Property) -> Int
            ExampleProvider::getCountAfterCursor, 
            // :(SomeType, Int?, Type of the Identifier-annotated Property)->List<Example>
            ExampleProvider::getPaginatedExamplesForParent 
        )
    }
    
    //...
}

Now, given a query that returns an instance of SomeType, let's call it something we can make use of the paginated property.

{
    something {
        exampleConnections(first: 2, after: "Mg==") {
            edges {
                cursor
                node {
                    someNumber
                }
            }
            pageInfo {
                lastCursor
                lastPage
            }    
            totalCount
        }
    }
} 

Paginated Queries

The usage of paginated queries produces a bit different schema, as direct access to the queried items is preferable to producing a connection-like schema.

Usage however is not more complicated because of this.

KGraphQL.schema {
    type<PageInfo>() // part of com.linkedplanet.plugin.graphqlplugin
    Example.registerTypes(this)
    type<Example>() {
        Example.cursorProperty(this) // provides a property 'cursor' on each object for use with the after-parameter

        property<Int>("totalCount") { // Not necessarily useful as it'd be repeated for each entry
            resolver { _ ->
                ExampleProvider.getExampleCount()
            }
        }
    }
    
    Example.paginatedQuery(
        this, 
        "allExamples", 
        0) // 0 is the default value for the parameter if no after-parameter is provided
    { first: Int, after: Int ->
        ExampleProvider.getExamples
    }
    //...
}

With this snippet, the query is fully usable

{
    allExamples(first: 1, after: "MQ==") {
        cursor
        someNumber
        totalCount
    }
} 

Alternative

One can also use the generated paginateInMemory function:

fun List<Example>.paginateInMemory(first: Int?, after: String?): ExampleConnection

It is more generally useful as it can also be used to build paginated queries in the schema. However, it also exposes one of the generated Types directly (ExampleConnection).

On Identifiers

Currently only values of type Int or String are fully supported to be annotated as Identifier. One can, however, easily extend the support by providing a parse function on the target-type:

fun TargetType.Companion.parse(s: String): TargetType = TODO()

The only requirement to this function is that

val target: TargetType // = ...
TargetType.parse(target.toString()) == target

must hold.

To come

  • Paginated queries helper
  • Non-connection type pagination (no edges or pageInfo)

About

Plugin for the Kotlin-compiler that generates boilerplate and utility functions for pagination in kgraphql.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages