Code Monkey home page Code Monkey logo

kgraphql's Introduction

KGraphQL

Maven Central Awesome Kotlin Badge Chat

KGraphQL is a Kotlin implementation of GraphQL. It provides a rich DSL to set up the GraphQL schema.

data class Article(val id: Int, val text: String)

fun main() {
    val schema = KGraphQL.schema {
        query("article") {
            resolver { id: Int?, text: String ->
                Article(id ?: -1, text)
            }
        }
        type<Article> {
            property<String>("fullText") {
                resolver { article: Article ->
                    "${article.id}: ${article.text}"
                }
            }
        }
    }

    schema.execute("""
        {
            article(id: 5, text: "Hello World") {
                id
                fullText
            }
        }
    """.trimIndent()).let(::println)
}

Documentation

See the documentation for a more detailed explanation of the library.

Contributing

All contributions are welcome. Feel free to open issues and PRs!

Building

To build KGraphQL you only need to have JDK8 installed. invoke

./gradlew build

To perform local build.

Versioning

The versioning is following Semantic Versioning

Links

Specification : http://facebook.github.io/graphql/

License

KGraphQL is Open Source software released under the MIT license

kgraphql's People

Contributors

alexeysoshin avatar allali84 avatar b-eyselein avatar bertrand avatar brunnogrillo avatar eschouten avatar ikasovitch avatar jeggy avatar kimar avatar lorefnon avatar maaxgr avatar mdnorman avatar nasindustrie avatar nathanpb avatar netag avatar nidomiro avatar nielsvanvelzen avatar nikkyai avatar pabl0rg avatar pgutkowski avatar rinkledink avatar ryanzidago avatar saintmalik avatar sondrele avatar sugasaki avatar swiftrs avatar thevietto avatar tobias-walle avatar vemilyus avatar xapphire13 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kgraphql's Issues

Suspendable resolvers by default

Instead of having both resolver and suspendResolver, we could do that we only have resolver which is suspendable always.

I don't see any downsides of doing it like this, and this would also kinda follow the patterns that ktor are doing with their routes.

Print Schema?

Copied from: pgutkowski#45

Would be great to be able to export the schema as SDL into a schema.graphql file.

Could maybe be an optional setting that could be done something like this

configure {
  exportSDL(file = File("schema.graphql"))
}

"operationName" should not be part of "variables"

Hi guys,

I just tried to test my graphQL-API with GraphiQL and everything worked fine.

But when I tried to define multiple queries/mutation in one body I got a GraphQLError "Must provide an operation name from: [op1, op2]"

I soon realized that you actually read the operationName out of the variables string I provide for the schema execution.
Wouldn't it be better to add a new argument "operationName" to the execute-function of the schema?

Otherwise I have to inject the operationName into the variables before calling the function.

Greetings,
Fabio

Compatibility issue: SchemaException for type with a UUID

I was switching from com.github.pgutkowski:kgraphql:0.3.0' to 'com.apurebase:kgraphql:0.5.1'.
After that I got the following exception:

Caused by: com.apurebase.kgraphql.schema.SchemaException: An Object type must define one or more fields. Found none on type UUID
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleObjectType(SchemaCompilation.kt:234)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.access$handleObjectType(SchemaCompilation.kt:32)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation$handleRawType$typeCreator$1.invoke(SchemaCompilation.kt:183)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation$handleRawType$typeCreator$1.invoke(SchemaCompilation.kt:32)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleRawType(SchemaCompilation.kt:190)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleSimpleType(SchemaCompilation.kt:161)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handlePossiblyWrappedType(SchemaCompilation.kt:149)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleKotlinProperty(SchemaCompilation.kt:301)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleObjectType(SchemaCompilation.kt:213)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.access$handleObjectType(SchemaCompilation.kt:32)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation$handleRawType$typeCreator$1.invoke(SchemaCompilation.kt:183)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation$handleRawType$typeCreator$1.invoke(SchemaCompilation.kt:32)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleRawType(SchemaCompilation.kt:190)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleSimpleType(SchemaCompilation.kt:161)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleCollectionType(SchemaCompilation.kt:156)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handlePossiblyWrappedType(SchemaCompilation.kt:145)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleOperation(SchemaCompilation.kt:132)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.handleQueries(SchemaCompilation.kt:113)
	at com.apurebase.kgraphql.schema.structure2.SchemaCompilation.perform(SchemaCompilation.kt:56)
	at com.apurebase.kgraphql.schema.dsl.SchemaBuilder.build(SchemaBuilder.kt:26)
	at com.apurebase.kgraphql.KGraphQL$Companion.schema(KGraphQL.kt:8)
	at timebox.graphql.GrapQLKt.<clinit>(GrapQL.kt:21)
	... 40 more

Unfortunatly it does not tell which type is causing the error, but I have just two classes with a UUID property. However, for these classes I have a property resolver that handles the UUID property:

type<TimeEntry> {
     description = "Time entries ..."

     property<String?>("id") {
         resolver { timeEntry ->
             timeEntry.id.toString()
         }
     }
   ...

the timeEntry.id is of type UUID.

Any idea!

Build Error in Java 11

I am attempting to build a local checkout of this project, but I get following error:

โžœ ./gradlew build 

Welcome to Gradle 5.6.3!

Here are the highlights of this release:
 - Incremental Groovy compilation
 - Groovy compile avoidance
 - Test fixtures for Java projects
 - Manage plugin versions via settings script

For more details see https://docs.gradle.org/5.6.3/release-notes.html

Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details
Configuration(s) specified but the install task does not exist in project :.
Configuration(s) specified but the install task does not exist in project :kgraphql.
Configuration(s) specified but the install task does not exist in project :kgraphql-ktor.

> Task :kgraphql:compileKotlin FAILED
e: .../kgraphql/src/main/kotlin/com/apurebase/kgraphql/request/Lexer.kt: (8, 37): Symbol is declared in module 'jdk.scripting.nashorn' which does not export package 'jdk.nashorn.internal.r
untime'
e: .../KGraphQL/kgraphql/src/main/kotlin/com/apurebase/kgraphql/request/Lexer.kt: (528, 26): Symbol is declared in module 'jdk.scripting.nashorn' which does not export package 'jdk.nashorn.internal
.runtime'
e: .../KGraphQL/kgraphql/src/main/kotlin/com/apurebase/kgraphql/request/Lexer.kt: (528, 40): Symbol is declared in module 'jdk.scripting.nashorn' which does not export package 'jdk.nashorn.internal
.runtime'

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':kgraphql:compileKotlin'.
> Compilation error. See log for more details

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 11s
4 actionable tasks: 1 executed, 3 up-to-date
โžœ java -version      
openjdk version "11.0.5" 2019-10-15 LTS
OpenJDK Runtime Environment Corretto-11.0.5.10.1 (build 11.0.5+10-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.5.10.1 (build 11.0.5+10-LTS, mixed mode)

Can you please advise how to fix this ?

It seems Lexer.kt uses some internal API that is not exposed.

DSL for defining interfaces

It would be nice to be able to define interface properties that are not actually part of the Kotlin interface.

Idea being something like this:

interface MyInterface
data class MyDataClass(val id: Int): MyInterface
...
interface<MyInterface> {
  property<String>("field1") // No default implementation and will run into runtime schema creation error if not implement in all types that implement this interface
  property<String>("field2") { // This property has a default implementation, but can be overwritten within implemented types
    resolver { a: MyInterface ->
      "FIELD-2"
    }
  }
}
type<MyDataClass> {
  property<String>("field1") {
    resolver { data: MyDataClass ->
      "FIELD-${data.id}"
    }
  }
}

java.lang.IllegalArgumentException: object is not an instance of declaring class

package com.test.data

import org.elasticsearch.search.SearchHit

public data class Trace(val traceID: String, val spans: ArrayList<Span>) {
    companion object {
        fun fromSearchHits(traceID: String, hits: Array<SearchHit>): Trace {
            val spansArray = hits.map { Span.fromSearchHit(it) }.toTypedArray<Span>()
            return Trace(traceID, ArrayList(spansArray.sortedBy { it.startTime }))
        }
    }
}

public data class Span(val traceID: String, val spanID: String, val parentSpanID: String?, val duration: Int, val startTime: Long, val operationName: String, val serviceName: String, val logs: ArrayList<LogPoint>?, val tags: ArrayList<Tag>?) {
    companion object {
        fun fromSearchHit(hit: SearchHit): Span {
            val source = hit.sourceAsMap
            val traceID = source.get("traceID") as String
            val spanID = source.get("spanID") as String
            val duration = source.get("duration") as Int
            val startTime = source.get("startTime") as Long
            val operationName = source.get("operationName") as String
            val serviceName = (source.get("process") as Map<String, Any>)["serviceName"] as String
            val parentSpan = (source.get("references") as ArrayList<Map<String, String>>).getOrElse(0) { emptyMap() }["spanID"]
            val logs = (source.get("logs") as ArrayList<LogPoint>?)
            val tags = (source.get("tags") as ArrayList<Tag>?)
            return Span(traceID, spanID, parentSpan, duration, startTime, operationName, serviceName, logs, tags)
        }
    }
}

public data class Tag(val key: String, val type: String, val value: String)

public data class LogPoint(val timestamp: Int, val fields: List<LogPointField>)

public data class LogPointField(val key: String, val value: String)
package com.test.app

...
    val schema = KGraphQL.schema { 
        configure { 
            useDefaultPrettyPrinter = true
        }

        query("findTrace") { 
            resolver { traceID: String ->
                esRepo.getTraceByID(traceID)
            }.withArgs { 
                arg<String> { name = "traceID"}
            }
        }
    }

    routing {
        post("/graphql") {
            try {
                val body = call.receive<GraphQLBody>()
                println(body.query)
                println(body.variables)
                val resp = schema.execute(body.query, Gson().toJson(body.variables))
                call.respond(resp)
            } catch(e: HttpStatusException) {
                call.respondTextWriter(ContentType("application", "json"), e.statusCode) { }
            }
        }
    }
...

Output from println:

query findTrace($traceID: String!) {
  findTrace(traceID: $traceID) {
    traceID
    spans {
      spanID
      tags {
        key
        value
        __typename
      }
      __typename
    }
    __typename
  }
}

{traceID=646851f15cb2dad1}

Output from exception:

2020-02-18 16:22:56.984 [DefaultDispatcher-worker-1] ERROR ktor.application - Unhandled: POST - /graphql
java.lang.IllegalArgumentException: object is not an instance of declaring class
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113)
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:106)
        at kotlin.reflect.jvm.internal.KProperty1Impl.get(KProperty1Impl.kt:35)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor.createPropertyNode(ParallelRequestExecutor.kt:269)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor.handleProperty(ParallelRequestExecutor.kt:232)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor.createObjectNode(ParallelRequestExecutor.kt:209)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor.createNode(ParallelRequestExecutor.kt:174)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor$createNode$valuesMap$1.invokeSuspend(ParallelRequestExecutor.kt:157)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor$createNode$valuesMap$1.invoke(ParallelRequestExecutor.kt)
        at com.apurebase.kgraphql.schema.execution.ParallelRequestExecutor$toMapAsync$2$invokeSuspend$$inlined$map$lambda$1.invokeSuspend(ParallelRequestExecutor.kt:82)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740)

Better error reporting on Void

Currently if I have done some mistake and return nothing or just a TODO("Not implemented yet"). I'm getting this error:

Caused by: com.apurebase.kgraphql.schema.SchemaException: An Object type must define one or more fields. Found none on type Void

in large schemas it's really hard to figure out which resolver is causing this.

Better CI Testing environment

Currently we are using GitLab and this isn't working for GitHub forks.

There is also some issue resulting in no tests being run as seen here on GitLab

What we want:

  • Able to run tests against Pull Requests
  • Publish new versions when creating a new tag
  • Build & Deploy documentation site

Introduce a ktor feature

Would be nice to make this into a multi project and create a ktor feature which adds a /graphql route. And would have some configuration support to allow graphiql support out of the box

Status:

Type mutation must define one or more fields.

When using graphql-cli to do query introspection on my endpoint I get the error GraphQL endpoint generated invalid schema: Type mutation must define one or more fields..

I am using KGraphQL v0.6.2 and I do not have mutations designed for my endpoint.

Any ideas how I can have query-only schema (no mutations) using KGraphQL?

Here is my schema definition:

class SandDriveSchema(private val storage: SandDriveDataContext) {
    val schema = schema {
        configure {
            useDefaultPrettyPrinter = true
        }

        query("well") {
            resolver { id: Int ->
                transaction {
                    storage.getWell(id).toModel()
                }
            }
        }

        query("truckload") {
            resolver { id: Int ->
                transaction {
                    storage.getTruckLoad(id).toModel()
                }
            }
        }

        query("user") {
            resolver { id: Int ->
                transaction {
                    storage.getUser(id).toModel()
                }
            }
        }

        type<Location>()
        type<SandType>()
        type<Stage>()
        type<TruckLoad>()
        type<TruckLoadEvent>()
        type<User>()
        type<UserLocation>()
        type<Well>()
    }
}

Support GraphQL Aliases

It would be nice to support GraphQL Aliases. I.e.

query getUsers {
  admins: users(role: admin) {
    id
    firstName
    lastName
    phone
    username
  }
  accountants: users(role: accountant) {
    id
    firstName
    lastName
    phone
    username
  }
}

This would currently fail with property admins on Query does not exist

Lexer parses only alphabetical characters

Some specific cases. I use not only alphabetical characters but also Koreans.
but lexers don't parse any letters without alphabetical characters.

let me show some codes, l make some schema with korean characters.

data class ํ•œ๊ธ€(val ๋ฒˆํ˜ธ:String, val ๋‚ด์šฉ:String)
query("ํ•œ๊ธ€") {
    resolver { ๋ฒˆํ˜ธ: String? ->
    ํ•œ๊ธ€("1234", "์•ˆ๋…•ํ•˜์„ธ์š”")
    }
  }

and I make some http request to use graqhQL
request

POST /client/graphql? HTTP/1.1
Host: localhost:8080

{
  "query": "{ ํ•œ๊ธ€ {๋ฒˆํ˜ธ, ๋‚ด์šฉ}}"
  ,"operationName": null
  ,"variables": null
}

and Errors
response

{
    "errors": [
        {
            "message":"Syntax Error: Cannot parse the unexpected character \"\ํ•œ\".",
            "errorType": "GraphQLError"
        }
    ]
}

It seems like lexers don't parse any letters without alphabetical characters.
com.apurebase.kgraphql.request.Lexer line 129 and line 130

private fun readToken(prev: Token): Token {
return when (val code = body[pos].toInt()) {
   ... codes
// A-Z _ a-z
            in (65..90), 95, in (97..122) -> readName(pos, col, prev)
   ... codes
}
}

And line 216 and line 217

private fun readName(start: Int, col: Int, prev: Token): Token {
... codes
while (code != null && (code == 95 || // -
                    (code in 48..57) || // 0-9
                    (code in 65..90) || // A-Z
                    (code in 97..122)   // a-z
                    )
... codes

it is easy to support just only korean characters with add some branch codes. but will occur 2 problems

  1. the Lexer seems porting of javascript code, is it right way to customize the Kotlin Lexer?
  2. that patch seems to temporary. more approach needs to support multilingual languages

inline classes support?

trying to use a inline class instead of eg. Int currently causes the following error
com.apurebase.kgraphql.RequestException: Missing selection set on property id of type ProjectID

Provide documentation on Context usage

Currently there is no documentation on how to use context and the NotIntrospected annotation.

I quickly wrote an example of how it could be used:

val query = """
	query fetchHelloLabel($country: String!) {
		hello(country: $country) {
			label
		}
	}
"""
val variables = """
	{"country": "English"}
"""
val user = User(id = 1, name = "Username")
val ctx = context {
	+user
}
schema.execute(query, variables, ctx)
...
// In your schema definition
query("hello") {
	resolver { country: String, ctx: Context ->
		val user = ctx.get<User>()
		Hello(label = "Hello ${user?.name ?: "unknown"}")
	}
}

NullPointerException when trying to specify default arguments for a property resolver

Here's a test that shows the issue I'm running into:

@Test
fun `property arguments should accept default values`() {
    val schema = defaultSchema {
        query("actor") {
            resolver {
                -> Actor("John Doe", age)
            }
        }

       type<Actor> {
            property<String>("greeting") {
                resolver { actor: Actor, suffix: String ->
                    "$suffix, ${actor.name}!"
                }.withArgs {
                    arg<String> { name = "suffix"; defaultValue = "Hello" }
                }
            }
        }
    }

    val request = """
        {
            actor {
                greeting
            }
        }
    """.trimIndent()

    val response = deserialize(schema.execute(request)) as Map<String, Any>
    assertThat(response, equalTo(mapOf<String, Any>(
            "data" to mapOf<String, Any>(
                    "actor" to mapOf<String, Any>(
                            "greeting" to "Hello, John Doe!"
                    )
            )
    )))
}

This throws:

java.lang.NullPointerException
	at com.apurebase.kgraphql.schema.dsl.PropertyDSL.addInputValues(PropertyDSL.kt:63)
	at com.apurebase.kgraphql.schema.dsl.ResolverDSL.withArgs(ResolverDSL.kt:10)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1$2$1.invoke(ArgumentsSpecificationTest.kt:117)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1$2$1.invoke(ArgumentsSpecificationTest.kt:13)
	at com.apurebase.kgraphql.schema.dsl.PropertyDSL.<init>(PropertyDSL.kt:13)
	at com.apurebase.kgraphql.schema.dsl.TypeDSL.property(TypeDSL.kt:55)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1$2.invoke(ArgumentsSpecificationTest.kt:114)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1$2.invoke(ArgumentsSpecificationTest.kt:13)
	at com.apurebase.kgraphql.schema.dsl.TypeDSL.<init>(TypeDSL.kt:76)
	at com.apurebase.kgraphql.schema.dsl.SchemaBuilder.type(SchemaBuilder.kt:104)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1.invoke(ArgumentsSpecificationTest.kt:143)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest$property arguments should accept default values$schema$1.invoke(ArgumentsSpecificationTest.kt:13)
	at com.apurebase.kgraphql.schema.dsl.SchemaBuilder.build(SchemaBuilder.kt:25)
	at com.apurebase.kgraphql.TestUtilsKt.defaultSchema(TestUtils.kt:51)
	at com.apurebase.kgraphql.specification.language.ArgumentsSpecificationTest.property arguments should accept default values(ArgumentsSpecificationTest.kt:106)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

The issue is that PropertyDSL's init block that calls block() is defined before the inputValues field is initialized.

Introduce a KGraphQL Kotlin Compiler Plugin

I don't have any compiler plugin experience, but I guess it would be possible to create a plugin that would validate the schema at compile time instead of runtime, making development safer.

Example in #71 we define a interface, and then when implementing that interface in a new type, we would get an error right within IntelliJ that we need to implemented the defined properties from the interface.

Error handling for mutations

Hi,

is there any support for handling errors like specified in the latest graphQL spec?
https://graphql.github.io/graphql-spec/June2018/#sec-Errors

Currently we try to delete multiple entries in our database through one bulk mutation.
The problem is that we are uncertain how the graphql-response should look like, when 1 or more entries could not be deleted because, e.g. the current user has the wrong permissions.

Thanks in advance for your answer :)

Variables are not supported

When creating a query like the following, I get a 500 error.

{
    "query":"query($id: Int!)
    {
        TruckLoad(id: $id){
            id
            truckload{id}
        }
    }",
    "variables":{
	"id": 5
    }
}

The endpoint throws an exception about a null argument

com.apurebase.kgraphql.RequestException: argument id is not optional, value cannot be null

However, if I specify a default variable, like the following, the endpoint returns, but only using the default value (2 in this example) instead of the defined variable (5 in this example)

{
    "query":"query($id: Int! = 2)
    {
        TruckLoad(id: $id){
            id
            truckload{id}
        }
    }",
    "variables":{
	"id": 5
    }
}

Better documentation

We should aim to have a better documentation page, which doesn't use the wiki, so pull requests are allowed for documentation also.

Implement a DataLoaderDSL for batching

Implement a DataLoader style batching feature.

I've been thinking about a structure like this:

...
data class Tree(val id: String, val parentId: String?)
...
type<Tree> {
    // String - defines the key that will be sent from the [prepare] into [loader]
    // List<Tree> - defines the return type that the [loader] is required to return.
    // the loader is then required to return it in a map format like Map<String, List<Tree>>
    dataProperty<String, List<Tree>>("children") {
        // Step 2: This will only be called once.
        loader { keys: List<String> ->
            keys.map{ id -> id to listOf(Tree("SomeId", id)) }.toMap()
        }
    
        // Step 1: This will be called for each node in the list, or only once if it's not a list
        prepare { parent: Tree -> parent.id }
    }
}

Anyone is welcome with some input on how this could be achieved.

Creating two fragments with same name should result in error or warning

Currently the Query

{
    film {
        ...film_title
    }
}

fragment film_title on Film {
    title
}

fragment film_title on Film {
    director {
        name
        age
    }
}

creates no error, but I think it should there should be at least a warning.
Here it's an error: https://metaphysics-production.artsy.net/?query=fragment%20a%20on%20Article%20%7B%0A%20%20id%0A%7D%0A%0Afragment%20a%20on%20Article%20%7B%0A%20%20cached%0A%7D%0A%0A%7B%0A%20%20article(id%3A%20%22%22)%7B%0A%20%20%20%20...a%0A%20%20%7D%0A%7D

Confusing `Generic types are not supported by GraphQL` error with Array

com.apurebase.kgraphql.schema.SchemaException: Generic types are not supported by GraphQL, found kotlin.Array<"XYZ"> is thrown when using data classes that have Array<xyz> as parameters. Changing the parameters to List<xyz> instead of Array<xyz> fixes the issue, but is very confusing given that List is also generic

Edit: further inspection shows this is because Array mustnt implement Iterable. Is there a possibility that support for Array could be added?

QueryOrMutationDSL.resolver(function) Support 7, 8, 9 parametres in function

fun <T, R, E, W, Q, A, S, B>resolver(function: suspend (R, E, W, Q, A, S, B) -> T) = resolver(FunctionWrapper.on(function))

fun <T, R, E, W, Q, A, S, B, U>resolver(function: suspend (R, E, W, Q, A, S, B, U) -> T) = resolver(FunctionWrapper.on(function))

fun <T, R, E, W, Q, A, S, B, U, C>resolver(function: suspend (R, E, W, Q, A, S, B, U, C) -> T) = resolver(FunctionWrapper.on(function))

Hanging suspendResolver

Schema.execute( .. ) will not return from suspendResolver { .. } if the result size is big "enough". This behaviour starts with switching from 0.3.1 to 0.4.0. Kgraphql runs in Ktor 1.2.1.
The suspendResolver { .. } is

fun SchemaBuilder<Unit>.timeEntry() {

    query("timeEntries") {
        suspendResolver { ctx: Context, year: Int?, month: Int?, userId: UserId?, topicFK: Int?, timeEntryTypeFK: Int? ->
            val range = dateRange(year, month)

            val result = transacted {
                selectTimeEntries(ctx.applicationContext(), ctx.applicationContext().userId(userId), range)
            }
                    .filterOn(topicFK != null) { it.topicFK == topicFK }
                    .filterOn(timeEntryTypeFK != null) { it.timeEntryTypeFK == timeEntryTypeFK }
            println(result)
            result
        }
    }
..

The result will be printed (for debugging) and but Schema.execute( .. ) will not return. When executing the test in debug mode and pausing execution the stacktrace looks like this:

park:-1, Unsafe (sun.misc)
parkNanos:215, LockSupport (java.util.concurrent.locks)
parkNanos:31, DefaultTimeSource (kotlinx.coroutines)
joinBlocking:83, BlockingCoroutine (kotlinx.coroutines)
runBlocking:54, BuildersKt__BuildersKt (kotlinx.coroutines)
runBlocking:1, BuildersKt (kotlinx.coroutines)
handleRequest:137, TestApplicationEngine (io.ktor.server.testing)
handleRequest:33, TestEngineKt (io.ktor.server.testing)
invoke:310, GraphQLTest$test get time entries$2 (timebox.graphql)
invoke:49, GraphQLTest$test get time entries$2 (timebox.graphql)
invoke:69, TestEngineKt$withTestApplication$1 (io.ktor.server.testing)
invoke:-1, TestEngineKt$withTestApplication$1 (io.ktor.server.testing)
withApplication:50, TestEngineKt (io.ktor.server.testing)
withApplication$default:44, TestEngineKt (io.ktor.server.testing)
withTestApplication:67, TestEngineKt (io.ktor.server.testing)
test get time entries:305, GraphQLTest (timebox.graphql)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeMethod:628, ReflectionUtils (org.junit.platform.commons.util)
invoke:117, ExecutableInvoker (org.junit.jupiter.engine.execution)
lambda$invokeTestMethod$7:184, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:-1, 165687172 (org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$287)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
invokeTestMethod:180, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:127, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:68, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
lambda$executeRecursively$5:135, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1278852808 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$176)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$7:125, NodeTestTask (org.junit.platform.engine.support.hierarchical)
invoke:-1, 9190301 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$175)
around:135, Node (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$8:123, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 966544353 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$174)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
executeRecursively:122, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:80, NodeTestTask (org.junit.platform.engine.support.hierarchical)
accept:-1, 1444635922 (org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$180)
forEach:1257, ArrayList (java.util)
invokeAll:38, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$5:139, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1278852808 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$176)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$7:125, NodeTestTask (org.junit.platform.engine.support.hierarchical)
invoke:-1, 9190301 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$175)
around:135, Node (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$8:123, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 966544353 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$174)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
executeRecursively:122, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:80, NodeTestTask (org.junit.platform.engine.support.hierarchical)
accept:-1, 1444635922 (org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$180)
forEach:1257, ArrayList (java.util)
invokeAll:38, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$5:139, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1278852808 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$176)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$7:125, NodeTestTask (org.junit.platform.engine.support.hierarchical)
invoke:-1, 9190301 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$175)
around:135, Node (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$8:123, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 966544353 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$174)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
executeRecursively:122, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:80, NodeTestTask (org.junit.platform.engine.support.hierarchical)
submit:32, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
execute:57, HierarchicalTestExecutor (org.junit.platform.engine.support.hierarchical)
execute:51, HierarchicalTestEngine (org.junit.platform.engine.support.hierarchical)
execute:229, DefaultLauncher (org.junit.platform.launcher.core)
lambda$execute$6:197, DefaultLauncher (org.junit.platform.launcher.core)
accept:-1, 319644606 (org.junit.platform.launcher.core.DefaultLauncher$$Lambda$135)
withInterceptedStreams:211, DefaultLauncher (org.junit.platform.launcher.core)
execute:191, DefaultLauncher (org.junit.platform.launcher.core)
execute:128, DefaultLauncher (org.junit.platform.launcher.core)
startRunnerWithArgs:69, JUnit5IdeaTestRunner (com.intellij.junit5)
startRunnerWithArgs:47, IdeaTestRunner$Repeater (com.intellij.rt.execution.junit)
prepareStreamsAndStart:242, JUnitStarter (com.intellij.rt.execution.junit)
main:70, JUnitStarter (com.intellij.rt.execution.junit)

If the data (result) returned from the selection is smaller the test passes. And it depends on the machine it is running on. And it also happens in production.

Syntax Error when leaving field list empty

Hi :)

I have a little issue within my server application after updating kgraphql from 0.8.1 to 0.9.1.
When calling a mutation function with an empty field list, kgraphql throws a GraphQLError: Syntax Error: Expected Name, found "}".
The mutation function handles a deletion of multiple objects within my database and in this case the client doesnt care about the return value of the mutation.
The mutation gets and returns a List of UUIDs. I also tried mapping the UUIDs to Strings but that didnt solve the problem either.

The mutation looks like this:

{
    "query": "mutation($ids: [UUID!]!) {
        r0: bulkDelete(value: $ids){
            # **This is empty**
        }
     }",
    "variables": "{
            "ids":[\"2828e080-d128-4c57-bcd8-6345007ed78e\",\"2a2283fc-fd6b-4e69-a660-66f188c2ace9\",\"70fccd23-5f96-429b-be54-87e1f8dd5d38\",\"7675f9c2-305f-4251-8d82-65dc6e5a9265\",\"7d4d1251-370f-4ea7-9b02-cd712205ea03\"]
        }"
}

This mutation worked perfectly fine in version 0.8.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.