Skip to content

Commit 8bcbf4e

Browse files
committed
Fix introspection issues, tested with graphdoc and graphql-docs
1 parent 8440aba commit 8bcbf4e

File tree

12 files changed

+99
-633
lines changed

12 files changed

+99
-633
lines changed

build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group 'com.github.pgutkowski'
2-
version '0.2.1'
2+
version '0.2.3-SNAPSHOT'
33

44
buildscript {
55
ext.kotlin_version = '1.1.3'
@@ -109,10 +109,10 @@ bintray {
109109
publications = ['MyPublication']
110110

111111
version {
112-
name = '0.2.1'
112+
name = '0.2.3'
113113
desc = 'KGraphQL alpha release'
114114
released = new Date()
115-
vcsTag = '0.2.1'
115+
vcsTag = '0.2.3'
116116
}
117117
}
118118

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/directive/Directive.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ data class Directive (
4444
*/
4545
val SKIP = Directive.Partial( "skip",
4646
listOf(FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT),
47-
DirectiveExecution(FunctionWrapper.on({ `if` : Boolean? -> DirectiveResult(!(`if` ?: false)) }))
47+
DirectiveExecution(FunctionWrapper.on({ `if` : Boolean -> DirectiveResult(!`if`) }))
4848
)
4949

5050
/**
@@ -53,7 +53,7 @@ data class Directive (
5353
*/
5454
val INCLUDE = Directive.Partial( "include",
5555
listOf(FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT),
56-
DirectiveExecution(FunctionWrapper.on({ `if` : Boolean? -> DirectiveResult(`if` ?: false) }))
56+
DirectiveExecution(FunctionWrapper.on({ `if` : Boolean -> DirectiveResult(`if`) }))
5757
)
5858
}
5959
}

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/dsl/QueryOrMutationDSL.kt

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import com.github.pgutkowski.kgraphql.schema.model.QueryDef
88

99
class QueryOrMutationDSL(val name : String, block : QueryOrMutationDSL.() -> Unit) : DepreciableItemDSL(), ResolverDSL.Target {
1010

11-
1211
private val inputValues = mutableListOf<InputValueDef<*>>()
1312

1413
init {

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/model/MutableSchemaDefinition.kt

+31-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import com.github.pgutkowski.kgraphql.schema.builtin.BUILT_IN_TYPE
66
import com.github.pgutkowski.kgraphql.schema.directive.Directive
77
import com.github.pgutkowski.kgraphql.schema.directive.DirectiveLocation
88
import com.github.pgutkowski.kgraphql.schema.dsl.TypeDSL
9-
import com.github.pgutkowski.kgraphql.schema.introspection.__Schema
109
import com.github.pgutkowski.kgraphql.schema.introspection.TypeKind
1110
import com.github.pgutkowski.kgraphql.schema.introspection.__Directive
1211
import com.github.pgutkowski.kgraphql.schema.introspection.__EnumValue
1312
import com.github.pgutkowski.kgraphql.schema.introspection.__Field
13+
import com.github.pgutkowski.kgraphql.schema.introspection.__Schema
1414
import com.github.pgutkowski.kgraphql.schema.introspection.__Type
1515
import kotlin.reflect.KClass
16-
import kotlin.reflect.KProperty1
1716
import kotlin.reflect.full.isSubclassOf
1817

1918
/**
@@ -23,7 +22,8 @@ import kotlin.reflect.full.isSubclassOf
2322
data class MutableSchemaDefinition (
2423
private val objects: ArrayList<TypeDef.Object<*>> = arrayListOf(
2524
TypeDef.Object(__Schema::class.defaultKQLTypeName(), __Schema::class),
26-
create__TypeDefinition()
25+
create__TypeDefinition(),
26+
create__DirectiveDefinition()
2727
),
2828
private val queries: ArrayList<QueryDef<*>> = arrayListOf(),
2929
private val scalars: ArrayList<TypeDef.Scalar<*>> = arrayListOf(
@@ -145,4 +145,31 @@ private fun create__TypeDefinition() = TypeDSL(emptyList(), __Type::class){
145145
transformation(__Type::enumValues){ enumValues: List<__EnumValue>?, includeDeprecated: Boolean? ->
146146
if (includeDeprecated == true) enumValues else enumValues?.filterNot { it.isDeprecated }
147147
}
148-
}.toKQLObject()
148+
}.toKQLObject()
149+
150+
private fun create__DirectiveDefinition() = TypeDSL(emptyList(), __Directive::class){
151+
property<Boolean>("onField"){
152+
resolver { dir: __Directive ->
153+
dir.locations.contains(DirectiveLocation.FIELD)
154+
}
155+
deprecate("Use `locations`.")
156+
}
157+
property<Boolean>("onFragment"){
158+
resolver { dir: __Directive -> dir.locations.containsAny (
159+
DirectiveLocation.FRAGMENT_SPREAD,
160+
DirectiveLocation.FRAGMENT_DEFINITION,
161+
DirectiveLocation.INLINE_FRAGMENT)
162+
}
163+
deprecate("Use `locations`.")
164+
}
165+
property<Boolean>("onOperation"){
166+
resolver{ dir : __Directive -> dir.locations.containsAny (
167+
DirectiveLocation.QUERY,
168+
DirectiveLocation.MUTATION,
169+
DirectiveLocation.SUBSCRIPTION)
170+
}
171+
deprecate("Use `locations`.")
172+
}
173+
}.toKQLObject()
174+
175+
private fun <T> List<T>.containsAny(vararg elements: T) = elements.filter { this.contains(it) }.any()

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/structure2/SchemaCompilation.kt

+13-8
Original file line numberDiff line numberDiff line change
@@ -185,21 +185,22 @@ class SchemaCompilation(val configuration : SchemaConfiguration, val definition
185185

186186
private fun handleObjectType(kClass: KClass<*>) : Type {
187187
assertValidObjectType(kClass)
188+
val objectDef = definition.objects.find { it.kClass == kClass } ?: TypeDef.Object(kClass.defaultKQLTypeName(), kClass)
188189

189-
val kind = if(kClass.isFinal){
190-
TypeKind.OBJECT
191-
} else {
192-
TypeKind.INTERFACE
193-
}
190+
//treat introspection types as objects -> adhere to reference implementation behaviour
191+
val kind = if(kClass.isFinal || objectDef.name.startsWith("__")) TypeKind.OBJECT else TypeKind.INTERFACE
194192

195-
val objectDef = definition.objects.find { it.kClass == kClass } ?: TypeDef.Object(kClass.defaultKQLTypeName(), kClass)
196193
val objectType = if(kind == TypeKind.OBJECT) Type.Object(objectDef) else Type.Interface(objectDef)
197194
val typeProxy = TypeProxy(objectType)
198195
queryTypeProxies.put(kClass, typeProxy)
199196

200197
val kotlinFields = kClass.memberProperties
201198
.filterNot { objectDef.isIgnored(it) }
202-
.map { property -> handleKotlinProperty(property, objectDef.kotlinProperties[property], objectDef.transformations[property]) }
199+
.map { property -> handleKotlinProperty (
200+
kProperty = property,
201+
kqlProperty = objectDef.kotlinProperties[property],
202+
transformation = objectDef.transformations[property]
203+
) }
203204

204205
val extensionFields = objectDef.extensionProperties.map { property -> handleOperation(property) }
205206

@@ -278,7 +279,11 @@ class SchemaCompilation(val configuration : SchemaConfiguration, val definition
278279
return InputValue(InputValueDef(kProperty.returnType.jvmErasure, kProperty.name), type)
279280
}
280281

281-
private fun <T : Any, R>handleKotlinProperty(kProperty: KProperty1<T, R>, kqlProperty: PropertyDef.Kotlin<*, *>?, transformation: Transformation<*, *>?) : Field.Kotlin<*, *>{
282+
private fun <T : Any, R> handleKotlinProperty (
283+
kProperty: KProperty1<T, R>,
284+
kqlProperty: PropertyDef.Kotlin<*, *>?,
285+
transformation: Transformation<*, *>?
286+
) : Field.Kotlin<*, *> {
282287
val returnType = handlePossiblyWrappedType(kProperty.returnType, TypeCategory.QUERY)
283288
val inputValues = if(transformation != null){
284289
handleInputValues("$kProperty transformation", transformation.transformation, emptyList())

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/structure2/SchemaModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ data class SchemaModel (
2525
val inputTypesByName = inputTypes.values.associate { it.name to it }
2626

2727
override val types: List<__Type> = allTypes.values.toList()
28-
.filterNot { it.name?.startsWith("__") ?: false }
2928
//workaround on the fact that Double and Float are treated as GraphQL Float
3029
.filterNot { it is Type.Scalar<*> && it.kClass == Float::class }
30+
//query and mutation must be present in introspection 'types' field for introspection tools
31+
.plus(mutation).plus(query)
3132

3233
override val queryType: __Type = query
3334

src/main/kotlin/com/github/pgutkowski/kgraphql/schema/structure2/Type.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ interface Type : __Type {
8989

9090
override val possibleTypes: List<__Type>? = null
9191

92-
override val interfaces: List<Type.Interface<*>>? = null
92+
override val interfaces: List<Type.Interface<*>>? = emptyList()
9393

9494
override fun isInstance(value: Any?): Boolean = false
9595
}

src/test/kotlin/com/github/pgutkowski/kgraphql/server/HttpRequestHandler.kt

+7-21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.netty.handler.codec.http.HttpResponseStatus
1515
import io.netty.handler.codec.http.HttpVersion
1616
import io.netty.handler.codec.http.QueryStringDecoder
1717
import io.netty.util.AsciiString
18+
import java.nio.charset.Charset
1819
import java.util.logging.Level
1920
import java.util.logging.Logger
2021

@@ -30,33 +31,18 @@ class HttpRequestHandler(val schema : DefaultSchema) : SimpleChannelInboundHandl
3031

3132
override fun channelRead0(ctx: ChannelHandlerContext, msg: FullHttpRequest) {
3233
when{
33-
msg.uri().startsWith("/graphql?") -> handleQuery(ctx, msg)
34-
msg.uri().startsWith("/graphql/docs") -> handleDocQuery(ctx, msg)
34+
msg.uri().startsWith("/graphql") -> handleQuery(ctx, msg)
3535
else -> exceptionCaught(ctx, IllegalArgumentException("Invalid path"))
3636
}
3737
}
3838

39-
fun handleDocQuery(ctx: ChannelHandlerContext, msg: FullHttpRequest){
40-
val path = msg.uri().substring("/graphql/docs".length).split('/').filter(String::isNotBlank)
41-
42-
val response = when {
43-
// path.isEmpty() -> schema.writeHomeHtml()
44-
// path[0] == "query" -> schema.writeQueriesHtml()
45-
// path[0] == "mutation" -> schema.writeMutationsHtml()
46-
// path[0] == "type" -> {
47-
// schema.writeTypeHtml(path.getOrElse(1, {throw IllegalArgumentException("Missing type name")}))
48-
// }
49-
else -> throw IllegalArgumentException("Illegal request")
50-
}
51-
52-
// writeResponse(ctx, response, AsciiString("text/html"))
53-
}
54-
5539
private fun handleQuery(ctx: ChannelHandlerContext, msg: FullHttpRequest) {
56-
val queryParameters = QueryStringDecoder(msg.uri()).parameters()["query"] ?: throw IllegalArgumentException("Please specify query")
57-
val query = if (queryParameters.size == 1) queryParameters.first() else throw IllegalArgumentException("Please specify only one query")
40+
val content = msg.content().toString(Charset.defaultCharset())
41+
val query = objectMapper.readTree(content)["query"].textValue()
42+
?: throw IllegalArgumentException("Please specify only one query")
5843
try {
59-
writeResponse(ctx, schema.execute(query, null))
44+
val response = schema.execute(query, null)
45+
writeResponse(ctx, response)
6046
} catch(e: Exception) {
6147
writeResponse(ctx, "{\"errors\" : { \"message\": \"Caught ${e.javaClass.canonicalName}: ${e.message?.replace("\"", "\\\"")}\"}}")
6248
}

0 commit comments

Comments
 (0)