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

Commit

Permalink
Merge pull request pgutkowski#33 from vemilyus/kotlin-1.3-release-fixes
Browse files Browse the repository at this point in the history
Kotlin 1.3 release fixes
  • Loading branch information
pgutkowski authored Nov 4, 2018
2 parents 01202f8 + 072b378 commit c173d26
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 51 deletions.
11 changes: 4 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ group 'com.github.pgutkowski'
version '0.3.0-beta'

buildscript {
ext.kotlin_version = '1.3.0-rc-116'
ext.kotlin_version = '1.3.0'

repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
maven {
url "http://dl.bintray.com/kotlin/kotlin-eap"
}
jcenter()
}
dependencies {
Expand Down Expand Up @@ -42,10 +39,10 @@ repositories {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.2-eap13'
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'

compile "com.fasterxml.jackson.core:jackson-databind:2.9.3"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.3"
compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.7"

compile "com.github.ben-manes.caffeine:caffeine:1.0.0"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.github.pgutkowski.kgraphql.configuration.SchemaConfiguration
import kotlinx.coroutines.CommonPool
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers


class SchemaConfigurationDSL {
var useDefaultPrettyPrinter: Boolean = false
var useCachingDocumentParser: Boolean = true
var objectMapper: ObjectMapper = jacksonObjectMapper()
var documentParserCacheMaximumSize : Long = 1000L
var acceptSingleValueAsArray : Boolean = true
var coroutineDispatcher: CoroutineDispatcher = CommonPool
var documentParserCacheMaximumSize: Long = 1000L
var acceptSingleValueAsArray: Boolean = true
var coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default

internal fun update(block : SchemaConfigurationDSL.() -> Unit) = block()
internal fun update(block: SchemaConfigurationDSL.() -> Unit) = block()

internal fun build() : SchemaConfiguration {
internal fun build(): SchemaConfiguration {
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, acceptSingleValueAsArray)
return SchemaConfiguration (
return SchemaConfiguration(
useCachingDocumentParser,
documentParserCacheMaximumSize,
objectMapper,
useDefaultPrettyPrinter,
coroutineDispatcher
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,38 @@ import com.github.pgutkowski.kgraphql.schema.scalar.serializeScalar
import com.github.pgutkowski.kgraphql.schema.structure2.Field
import com.github.pgutkowski.kgraphql.schema.structure2.InputValue
import com.github.pgutkowski.kgraphql.schema.structure2.Type
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.defer
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KProperty1


@Suppress("UNCHECKED_CAST") // For valid structure there is no risk of ClassCastException
class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor, CoroutineScope {

data class ExecutionContext(val variables: Variables, val requestContext: Context)

override val coroutineContext: CoroutineContext = Job()

private val argumentsHandler = ArgumentsHandler(schema)

private val jsonNodeFactory = JsonNodeFactory.instance

private val dispatcher = schema.configuration.coroutineDispatcher

private val objectWriter = schema.configuration.objectMapper.writer().let {
if(schema.configuration.useDefaultPrettyPrinter){
if (schema.configuration.useDefaultPrettyPrinter) {
it.withDefaultPrettyPrinter()
} else {
it
}
}

override suspend fun suspendExecute(plan : ExecutionPlan, variables: VariablesJson, context: Context) : String {
val root = jsonNodeFactory.objectNode()
override suspend fun suspendExecute(plan: ExecutionPlan, variables: VariablesJson, context: Context): String {
val root = jsonNodeFactory.objectNode()
val data = root.putObject("data")
val channel = Channel<Pair<Execution, JsonNode>>()
val jobs = plan
Expand All @@ -71,27 +75,27 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
try {
val (execution, jsonNode) = channel.receive()
resultMap.put(execution, jsonNode)
} catch(e : Exception){
jobs.forEach{ it.cancel() }
} catch (e: Exception) {
jobs.forEach { it.cancel() }
throw e
}
}
channel.close()

for(operation in plan){
for (operation in plan) {
data.set(operation.aliasOrKey, resultMap[operation])
}

return objectWriter.writeValueAsString(root)
}

override fun execute(plan : ExecutionPlan, variables: VariablesJson, context: Context) : String = runBlocking {
override fun execute(plan: ExecutionPlan, variables: VariablesJson, context: Context): String = runBlocking {
suspendExecute(plan, variables, context)
}

private suspend fun <T>writeOperation(ctx: ExecutionContext, node: Execution.Node, operation: FunctionWrapper<T>) : JsonNode {
private suspend fun <T> writeOperation(ctx: ExecutionContext, node: Execution.Node, operation: FunctionWrapper<T>): JsonNode {
node.field.checkAccess(null, ctx.requestContext)
val operationResult : T? = operation.invoke (
val operationResult: T? = operation.invoke(
funName = node.field.name,
receiver = null,
inputValues = node.field.arguments,
Expand All @@ -105,7 +109,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
private suspend fun <T> createUnionOperationNode(ctx: ExecutionContext, parent: T, node: Execution.Union, unionProperty: Field.Union<T>): JsonNode {
node.field.checkAccess(parent, ctx.requestContext)

val operationResult : Any? = unionProperty.invoke(
val operationResult: Any? = unionProperty.invoke(
funName = unionProperty.name,
receiver = parent,
inputValues = node.field.arguments,
Expand All @@ -115,21 +119,21 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {

val returnType = unionProperty.returnType.possibleTypes.find { it.isInstance(operationResult) }

if(returnType == null) throw ExecutionException (
"Unexpected type of union property value, expected one of : ${unionProperty.type.possibleTypes }." +
if (returnType == null) throw ExecutionException(
"Unexpected type of union property value, expected one of : ${unionProperty.type.possibleTypes}." +
" value was $operationResult"
)

return createNode(ctx, operationResult, node, returnType)
}

private suspend fun <T> createNode(ctx: ExecutionContext, value : T?, node: Execution.Node, returnType: Type) : JsonNode {
private suspend fun <T> createNode(ctx: ExecutionContext, value: T?, node: Execution.Node, returnType: Type): JsonNode {
return when {
value == null -> createNullNode(node, returnType)

//check value, not returnType, because this method can be invoked with element value
value is Collection<*> -> {
if(returnType.isList()){
if (returnType.isList()) {
val arrayNode = jsonNodeFactory.arrayNode(value.size)
value.forEach { element -> arrayNode.add(createNode(ctx, element, node, returnType.unwrapList())) }
arrayNode
Expand All @@ -153,7 +157,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
}
}

private fun <T> createSimpleValueNode(returnType: Type, value: T) : JsonNode {
private fun <T> createSimpleValueNode(returnType: Type, value: T): JsonNode {
val unwrapped = returnType.unwrapped()
return when (unwrapped) {
is Type.Scalar<*> -> {
Expand All @@ -175,10 +179,10 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
}
}

private suspend fun <T> createObjectNode(ctx: ExecutionContext, value : T, node: Execution.Node, type: Type): ObjectNode {
private suspend fun <T> createObjectNode(ctx: ExecutionContext, value: T, node: Execution.Node, type: Type): ObjectNode {
val objectNode = jsonNodeFactory.objectNode()
for(child in node.children){
if(child is Execution.Fragment){
for (child in node.children) {
if (child is Execution.Fragment) {
objectNode.setAll(handleFragment(ctx, value, child))
} else {
val (key, jsonNode) = handleProperty(ctx, value, child, type)
Expand All @@ -190,11 +194,11 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {

private suspend fun <T> handleProperty(ctx: ExecutionContext, value: T, child: Execution, type: Type): Pair<String, JsonNode?> {
when (child) {
//Union is subclass of Node so check it first
//Union is subclass of Node so check it first
is Execution.Union -> {
val field = type.unwrapped()[child.key]
?: throw IllegalStateException("Execution unit ${child.key} is not contained by operation return type")
if(field is Field.Union<*>){
if (field is Field.Union<*>) {
return child.aliasOrKey to createUnionOperationNode(ctx, value, child, field as Field.Union<T>)
} else {
throw ExecutionException("Unexpected non-union field for union execution node")
Expand All @@ -215,10 +219,10 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
val expectedType = container.condition.type
val include = determineInclude(ctx, container.directives)

if(include){
if(expectedType.kind == TypeKind.OBJECT || expectedType.kind == TypeKind.INTERFACE){
if(expectedType.isInstance(value)){
return container.elements.map { handleProperty(ctx, value, it, expectedType)}.toMap()
if (include) {
if (expectedType.kind == TypeKind.OBJECT || expectedType.kind == TypeKind.INTERFACE) {
if (expectedType.isInstance(value)) {
return container.elements.map { handleProperty(ctx, value, it, expectedType) }.toMap()
}
} else {
throw IllegalStateException("fragments can be specified on object types, interfaces, and unions")
Expand All @@ -228,18 +232,18 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
return emptyMap()
}

private suspend fun <T> createPropertyNode(ctx: ExecutionContext, parentValue: T, node: Execution.Node, field: Field) : JsonNode? {
private suspend fun <T> createPropertyNode(ctx: ExecutionContext, parentValue: T, node: Execution.Node, field: Field): JsonNode? {
val include = determineInclude(ctx, node.directives)
node.field.checkAccess(parentValue, ctx.requestContext)

if(include){
when(field){
is Field.Kotlin<*,*> -> {
if (include) {
when (field) {
is Field.Kotlin<*, *> -> {
field.kProperty as KProperty1<T, *>
val rawValue = field.kProperty.get(parentValue)
val value : Any?
value = if(field.transformation != null){
field.transformation.invoke (
val value: Any?
value = if (field.transformation != null) {
field.transformation.invoke(
funName = field.name,
receiver = rawValue,
inputValues = field.arguments,
Expand All @@ -263,7 +267,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
}
}

suspend fun <T>handleFunctionProperty(ctx: ExecutionContext, parentValue: T, node: Execution.Node, field: Field.Function<*, *>) : JsonNode {
suspend fun <T> handleFunctionProperty(ctx: ExecutionContext, parentValue: T, node: Execution.Node, field: Field.Function<*, *>): JsonNode {
val result = field.invoke(
funName = field.name,
receiver = parentValue,
Expand All @@ -282,7 +286,8 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
receiver = null,
args = arguments,
ctx = ctx
)?.include ?: throw ExecutionException("Illegal directive implementation returning null result")
)?.include
?: throw ExecutionException("Illegal directive implementation returning null result")
}?.reduce { acc, b -> acc && b } ?: true
}

Expand All @@ -296,7 +301,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor {
val transformedArgs = argumentsHandler.transformArguments(funName, inputValues, args, ctx.variables, ctx.requestContext)

//exceptions are not caught on purpose to pass up business logic errors
return if(hasReceiver){
return if (hasReceiver) {
suspendInvoke(receiver, *transformedArgs.toTypedArray())
} else {
suspendInvoke(*transformedArgs.toTypedArray())
Expand Down

0 comments on commit c173d26

Please sign in to comment.