From b686929e8ab010ea689bc76cfb95a45784397f1d Mon Sep 17 00:00:00 2001 From: FFN <> Date: Fri, 3 Dec 2021 23:12:47 +0900 Subject: [PATCH 1/6] Added extensions to error response json --- .../com/apurebase/kgraphql/GraphQLSchema.kt | 16 +++++ .../com/apurebase/kgraphql/KtorFeature.kt | 60 +++++++++++++++++++ .../com/apurebase/kgraphql/GraphQLError.kt | 7 ++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt index 1b6d94a7..28bd54b1 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt @@ -20,6 +20,22 @@ fun SchemaBuilder.ufoSchema() { deserialize = { dateString -> LocalDate.parse(dateString) } } + /** + * ima9dan Added extensions to error response json + */ + query ("testError") { + description = "Returns a subset of the UFO Sighting records" + resolver { -> + try { + throw Exception("dfada") + } catch (e:Exception) { + val stackList: Array = e.stackTrace + throw GraphQLError("fasdfas",null,null,null,null, + mapOf("aa" to null,"bb" to 1, "cc" to stackList, "gaga" to arrayListOf("22","ss",32,"333"), "ggg" to mapOf("aa" to "adad","bb" to arrayOf("22","ss",32,"333"), "gaga" to listOf("22","ss",32,"333")))) + } + } + } + query("sightings") { description = "Returns a subset of the UFO Sighting records" diff --git a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt index 23ac8325..b2e3c177 100644 --- a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt +++ b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt @@ -90,6 +90,48 @@ class GraphQL(val schema: Schema) { return GraphQL(schema) } + /** + * ima9dan Added extensions to error response json + * TODO If you're having trouble writing this here, move it elsewhere + */ + fun Collection<*>.toJsonElement(): JsonElement { + val list: MutableList = mutableListOf() + this.forEach { + val value = it as? Any ?: return@forEach + when(value) { + is Number -> list.add(JsonPrimitive(value)) + is Boolean -> list.add(JsonPrimitive(value)) + is String -> list.add(JsonPrimitive(value)) + is Map<*, *> -> list.add((value).toJsonElement()) + is Collection<*> -> list.add(value.toJsonElement()) + is Array<*> -> list.add(value.toList().toJsonElement()) + else -> list.add(JsonPrimitive(value.toString())) // other type + } + } + return JsonArray(list) + } + /** + * ima9dan Added extensions to error response json + * TODO If you're having trouble writing this here, move it elsewhere + */ + fun Map<*, *>.toJsonElement(): JsonElement { + val map: MutableMap = mutableMapOf() + this.forEach { + val key = it.key as? String ?: return@forEach + val value = it.value ?: return@forEach + when(value) { + is Number? -> JsonPrimitive(value) + is Boolean? -> JsonPrimitive(value) + is String? -> JsonPrimitive(value) + is Map<*, *> -> map[key] = (value).toJsonElement() + is Collection<*> -> map[key] = value.toJsonElement() + is Array<*> -> map[key] = value.toList().toJsonElement() + else -> map[key] = JsonPrimitive(value.toString()) // other type + } + } + return JsonObject(map) + } + private fun GraphQLError.serialize(): String = buildJsonObject { put("errors", buildJsonArray { addJsonObject { @@ -105,6 +147,24 @@ class GraphQL(val schema: Schema) { put("path", buildJsonArray { // TODO: Build this path. https://spec.graphql.org/June2018/#example-90475 }) + /** + * ima9dan Added extensions to error response json + */ + extensions?.let { + put("extensions", buildJsonObject { + it.forEach { (key, value) -> + when(value) { + is Number? -> put(key, value) + is String? -> put(key, value) + is Boolean? -> put(key, value) + is Map<*,*> -> put(key, value.toJsonElement()) + is Collection<*> -> put(key, value.toJsonElement()) + is Array<*> -> put(key, value.toList().toJsonElement()) + else -> put(key, JsonPrimitive(value.toString())) // other type + } + } + }) + } } }) }.toString() diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt index 45723413..d2bd4a57 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt @@ -33,7 +33,12 @@ open class GraphQLError( /** * The original error thrown from a field resolver during execution. */ - val originalError: Throwable? = null + val originalError: Throwable? = null, + + /** + * ima9dan Added extensions to error response json + */ + val extensions: Map? = null ) : Exception(message) { constructor(message: String, node: ASTNode?) : this(message, nodes = node?.let(::listOf)) From e29e127233c7c5d8f175fa00d5365816c8dfbe31 Mon Sep 17 00:00:00 2001 From: FFN <> Date: Sun, 5 Dec 2021 23:44:21 +0900 Subject: [PATCH 2/6] update --- .../com/apurebase/kgraphql/Application.kt | 1 + .../com/apurebase/kgraphql/GraphQLSchema.kt | 18 +++---- kgraphql-ktor/build.gradle.kts | 1 + .../com/apurebase/kgraphql/KtorFeature.kt | 47 ++++++++++++------- kgraphql/build.gradle.kts | 1 + .../com/apurebase/kgraphql/GraphQLError.kt | 30 +++++++++++- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt index 46df522f..488f7a50 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt @@ -23,6 +23,7 @@ fun Application.module() { install(GraphQL) { useDefaultPrettyPrinter = true playground = true + debug = true endpoint = "/" wrap { diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt index 28bd54b1..bfcf17b9 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt @@ -23,16 +23,17 @@ fun SchemaBuilder.ufoSchema() { /** * ima9dan Added extensions to error response json */ - query ("testError") { + query("testError") { description = "Returns a subset of the UFO Sighting records" resolver { -> - try { - throw Exception("dfada") - } catch (e:Exception) { - val stackList: Array = e.stackTrace - throw GraphQLError("fasdfas",null,null,null,null, - mapOf("aa" to null,"bb" to 1, "cc" to stackList, "gaga" to arrayListOf("22","ss",32,"333"), "ggg" to mapOf("aa" to "adad","bb" to arrayOf("22","ss",32,"333"), "gaga" to listOf("22","ss",32,"333")))) - } +// try { + throw GraphQLError("dfada") +// } catch (e:Exception) { +// val stackList: Array = e.stackTrace +// throw GraphQLError("fasdfas",null,null,null,null, +// mapOf("aa" to null,"bb" to 1, "cc" to stackList, "gaga" to arrayListOf("22","ss",32,"333"), "ggg" to mapOf("aa" to "adad","bb" to arrayOf("22","ss",32,"333"), "gaga" to listOf("22","ss",32,"333")))) +// } + User(4, "dd") } } @@ -117,3 +118,4 @@ fun SchemaBuilder.ufoSchema() { } } } + diff --git a/kgraphql-ktor/build.gradle.kts b/kgraphql-ktor/build.gradle.kts index 5b15a615..e17de0b4 100644 --- a/kgraphql-ktor/build.gradle.kts +++ b/kgraphql-ktor/build.gradle.kts @@ -1,5 +1,6 @@ plugins { base + id("com.github.johnrengelman.shadow") version "7.1.0" kotlin("jvm") version "1.5.10" kotlin("plugin.serialization") version "1.5.0" id("org.jetbrains.dokka") version "1.4.32" diff --git a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt index b2e3c177..f5b1d9b3 100644 --- a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt +++ b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt @@ -1,5 +1,6 @@ package com.apurebase.kgraphql +import com.apurebase.kgraphql.GraphQL.Feature.toJsonElement import com.apurebase.kgraphql.schema.Schema import com.apurebase.kgraphql.schema.dsl.SchemaBuilder import com.apurebase.kgraphql.schema.dsl.SchemaConfigurationDSL @@ -27,6 +28,8 @@ class GraphQL(val schema: Schema) { var endpoint: String = "/graphql" + var debug: Boolean = true + fun context(block: ContextBuilder.(ApplicationCall) -> Unit) { contextSetup = block } @@ -83,7 +86,7 @@ class GraphQL(val schema: Schema) { } } catch (e: Throwable) { if (e is GraphQLError) { - context.respond(HttpStatusCode.OK, e.serialize()) + context.respond(HttpStatusCode.OK, e.serialize(config)) } else throw e } } @@ -131,15 +134,34 @@ class GraphQL(val schema: Schema) { } return JsonObject(map) } + /** + * ima9dan Added extensions to error response json + * TODO If you're having trouble writing this here, move it elsewhere + */ + fun buldJsonObjectByMap(it:Map):JsonObject { + return buildJsonObject { + it.forEach { (key, value) -> + when(value) { + is Number? -> put(key, value) + is String? -> put(key, value) + is Boolean? -> put(key, value) + is Map<*,*> -> put(key, value.toJsonElement()) + is Collection<*> -> put(key, value.toJsonElement()) + is Array<*> -> put(key, value.toList().toJsonElement()) + else -> put(key, JsonPrimitive(value.toString())) // other type + } + } + } + } - private fun GraphQLError.serialize(): String = buildJsonObject { + private fun GraphQLError.serialize(configure: Configuration): String = buildJsonObject { put("errors", buildJsonArray { addJsonObject { put("message", message) put("locations", buildJsonArray { locations?.forEach { addJsonObject { - put("line", it.line) + put("liane", it.line) put("column", it.column) } } @@ -151,19 +173,12 @@ class GraphQL(val schema: Schema) { * ima9dan Added extensions to error response json */ extensions?.let { - put("extensions", buildJsonObject { - it.forEach { (key, value) -> - when(value) { - is Number? -> put(key, value) - is String? -> put(key, value) - is Boolean? -> put(key, value) - is Map<*,*> -> put(key, value.toJsonElement()) - is Collection<*> -> put(key, value.toJsonElement()) - is Array<*> -> put(key, value.toList().toJsonElement()) - else -> put(key, JsonPrimitive(value.toString())) // other type - } - } - }) + put("extensions", buldJsonObjectByMap(it)) + } + if (configure.debug) { + debugInfo().let { + put("debug", buldJsonObjectByMap(it)) + } } } }) diff --git a/kgraphql/build.gradle.kts b/kgraphql/build.gradle.kts index e420407d..36d0a67a 100644 --- a/kgraphql/build.gradle.kts +++ b/kgraphql/build.gradle.kts @@ -1,6 +1,7 @@ plugins { base + id("com.github.johnrengelman.shadow") version "7.1.0" kotlin("jvm") version "1.5.10" id("org.jetbrains.dokka") version "1.4.32" signing diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt index d2bd4a57..261886f1 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt @@ -38,7 +38,8 @@ open class GraphQLError( /** * ima9dan Added extensions to error response json */ - val extensions: Map? = null + val extensionsErrorCode: String? = "INTERNAL", + val extensionsErrorDetail: Map? = null ) : Exception(message) { constructor(message: String, node: ASTNode?) : this(message, nodes = node?.let(::listOf)) @@ -59,6 +60,33 @@ open class GraphQLError( } } + val extensions: Map?by lazy { + val extensions = mutableMapOf() + extensionsErrorCode?.let{ extensions.put("code",extensionsErrorCode) } + extensionsErrorDetail?.let { extensions.put("detail",extensionsErrorDetail) } + extensions + } + + /** + * use only debug + */ + fun debugInfo(): Map { + val exception = mutableMapOf() + val stackList = this.stackTrace + if (!stackList[0].fileName.isNullOrEmpty()) { + exception.put("fileName",stackList[0].fileName) + exception.put("line",stackList[0].lineNumber.toString()) + } + if (!stackList[0].methodName.isNullOrEmpty()) { + exception.put("method",stackList[0].methodName) + } + if (!stackList[0].className.isNullOrEmpty()) { + exception.put("classPath", stackList[0].className) + } + exception.put("stackTrace", stackList) + return exception + } + fun prettyPrint(): String { var output = message ?: "" From 18651ad6c281fa247b282ebf04d57b9c7f0a2506 Mon Sep 17 00:00:00 2001 From: FFN <> Date: Mon, 6 Dec 2021 02:39:05 +0900 Subject: [PATCH 3/6] 1. Added extensions To the error response.extensions type is Map? 2. Added "error type" to make it easy to branch by error types at the client side. 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. 4. Embed serialize function to GraphQLError class because we want to use GraphQLError individually. ex: at StatusPages Plugin. --- .../com/apurebase/kgraphql/GraphQLSchema.kt | 4 +- .../com/apurebase/kgraphql/KtorFeature.kt | 110 ++------------ .../com/apurebase/kgraphql/GraphQLError.kt | 137 +++++++++++++++--- 3 files changed, 134 insertions(+), 117 deletions(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt index bfcf17b9..20b35e53 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt @@ -27,7 +27,9 @@ fun SchemaBuilder.ufoSchema() { description = "Returns a subset of the UFO Sighting records" resolver { -> // try { - throw GraphQLError("dfada") + val ex = GraphQLError("dasdfasd","UNAUTHORIZE", mapOf("test" to arrayOf("22",23))) + val ee = ex.serialize(true) +// throw GraphQLError("dfada") // } catch (e:Exception) { // val stackList: Array = e.stackTrace // throw GraphQLError("fasdfas",null,null,null,null, diff --git a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt index f5b1d9b3..c3b8b91a 100644 --- a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt +++ b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt @@ -1,6 +1,5 @@ package com.apurebase.kgraphql -import com.apurebase.kgraphql.GraphQL.Feature.toJsonElement import com.apurebase.kgraphql.schema.Schema import com.apurebase.kgraphql.schema.dsl.SchemaBuilder import com.apurebase.kgraphql.schema.dsl.SchemaConfigurationDSL @@ -11,7 +10,6 @@ import io.ktor.response.* import io.ktor.routing.* import io.ktor.util.* import kotlinx.coroutines.coroutineScope -import kotlinx.serialization.json.* import kotlinx.serialization.json.Json.Default.decodeFromString class GraphQL(val schema: Schema) { @@ -28,7 +26,11 @@ class GraphQL(val schema: Schema) { var endpoint: String = "/graphql" - var debug: Boolean = true + /** + * ima9dan + * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. + */ + var debug: Boolean = false fun context(block: ContextBuilder.(ApplicationCall) -> Unit) { contextSetup = block @@ -86,103 +88,21 @@ class GraphQL(val schema: Schema) { } } catch (e: Throwable) { if (e is GraphQLError) { - context.respond(HttpStatusCode.OK, e.serialize(config)) + /** + * ima9dan + * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. + * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. + */ + context.respond(HttpStatusCode.OK, e.serialize(config.debug)) } else throw e } } return GraphQL(schema) } - - /** - * ima9dan Added extensions to error response json - * TODO If you're having trouble writing this here, move it elsewhere - */ - fun Collection<*>.toJsonElement(): JsonElement { - val list: MutableList = mutableListOf() - this.forEach { - val value = it as? Any ?: return@forEach - when(value) { - is Number -> list.add(JsonPrimitive(value)) - is Boolean -> list.add(JsonPrimitive(value)) - is String -> list.add(JsonPrimitive(value)) - is Map<*, *> -> list.add((value).toJsonElement()) - is Collection<*> -> list.add(value.toJsonElement()) - is Array<*> -> list.add(value.toList().toJsonElement()) - else -> list.add(JsonPrimitive(value.toString())) // other type - } - } - return JsonArray(list) - } - /** - * ima9dan Added extensions to error response json - * TODO If you're having trouble writing this here, move it elsewhere - */ - fun Map<*, *>.toJsonElement(): JsonElement { - val map: MutableMap = mutableMapOf() - this.forEach { - val key = it.key as? String ?: return@forEach - val value = it.value ?: return@forEach - when(value) { - is Number? -> JsonPrimitive(value) - is Boolean? -> JsonPrimitive(value) - is String? -> JsonPrimitive(value) - is Map<*, *> -> map[key] = (value).toJsonElement() - is Collection<*> -> map[key] = value.toJsonElement() - is Array<*> -> map[key] = value.toList().toJsonElement() - else -> map[key] = JsonPrimitive(value.toString()) // other type - } - } - return JsonObject(map) - } - /** - * ima9dan Added extensions to error response json - * TODO If you're having trouble writing this here, move it elsewhere - */ - fun buldJsonObjectByMap(it:Map):JsonObject { - return buildJsonObject { - it.forEach { (key, value) -> - when(value) { - is Number? -> put(key, value) - is String? -> put(key, value) - is Boolean? -> put(key, value) - is Map<*,*> -> put(key, value.toJsonElement()) - is Collection<*> -> put(key, value.toJsonElement()) - is Array<*> -> put(key, value.toList().toJsonElement()) - else -> put(key, JsonPrimitive(value.toString())) // other type - } - } - } - } - - private fun GraphQLError.serialize(configure: Configuration): String = buildJsonObject { - put("errors", buildJsonArray { - addJsonObject { - put("message", message) - put("locations", buildJsonArray { - locations?.forEach { - addJsonObject { - put("liane", it.line) - put("column", it.column) - } - } - }) - put("path", buildJsonArray { - // TODO: Build this path. https://spec.graphql.org/June2018/#example-90475 - }) - /** - * ima9dan Added extensions to error response json - */ - extensions?.let { - put("extensions", buldJsonObjectByMap(it)) - } - if (configure.debug) { - debugInfo().let { - put("debug", buldJsonObjectByMap(it)) - } - } - } - }) - }.toString() } + /** + * ima9dan : Delete serialize function because.. + * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. + */ } diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt index 261886f1..9d7bf951 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt @@ -3,6 +3,7 @@ package com.apurebase.kgraphql import com.apurebase.kgraphql.schema.model.ast.ASTNode import com.apurebase.kgraphql.schema.model.ast.Location.Companion.getLocation import com.apurebase.kgraphql.schema.model.ast.Source +import kotlinx.serialization.json.* open class GraphQLError( @@ -36,13 +37,16 @@ open class GraphQLError( val originalError: Throwable? = null, /** - * ima9dan Added extensions to error response json - */ - val extensionsErrorCode: String? = "INTERNAL", + * ima9dan + * 1. Added extensions To the error response.extensions type is Map? + * 2. Added "error type" to make it easy to branch by error types at the client side. + */ + val extensionsErrorType: String? = "INTERNAL", val extensionsErrorDetail: Map? = null ) : Exception(message) { constructor(message: String, node: ASTNode?) : this(message, nodes = node?.let(::listOf)) + constructor(message: String, extensionsErrorType: String?,extensionsErrorDetail:Map?) : this(message,null,null,null,null,extensionsErrorType,extensionsErrorDetail ) /** * An array of { line, column } locations within the source GraphQL document @@ -60,17 +64,47 @@ open class GraphQLError( } } - val extensions: Map?by lazy { + fun prettyPrint(): String { + var output = message ?: "" + + if (nodes != null) { + for (node in nodes) { + if (node.loc != null) { + output += "\n\n" + node.loc!!.printLocation() + } + } + } else if (source != null && locations != null) { + for (location in locations!!) { + output += "\n\n" + source.print(location) + } + } + + return output + } + + /** + * ima9dan + * 1. Added extensions To the error response.extensions type is Map? + * 2. Added "error type" to make it easy to branch by error types at the client side. + * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. + * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. + */ + open val extensions: Map?by lazy { val extensions = mutableMapOf() - extensionsErrorCode?.let{ extensions.put("code",extensionsErrorCode) } + extensionsErrorType?.let{ extensions.put("type",extensionsErrorType) } extensionsErrorDetail?.let { extensions.put("detail",extensionsErrorDetail) } extensions } - /** - * use only debug - */ - fun debugInfo(): Map { + open fun extensionsDebug(): Map { + val extensions = mutableMapOf() + extensionsErrorType?.let{ extensions.put("type",extensionsErrorType) } + extensionsErrorDetail?.let { extensions.put("detail",extensionsErrorDetail) } + extensions.put("debug",this.debugInfo()) + return extensions + } + + open fun debugInfo(): Map { val exception = mutableMapOf() val stackList = this.stackTrace if (!stackList[0].fileName.isNullOrEmpty()) { @@ -87,21 +121,82 @@ open class GraphQLError( return exception } - fun prettyPrint(): String { - var output = message ?: "" - - if (nodes != null) { - for (node in nodes) { - if (node.loc != null) { - output += "\n\n" + node.loc!!.printLocation() - } + protected fun Collection<*>.toJsonElement(): JsonElement { + val list: MutableList = mutableListOf() + this.forEach { + val value = it as? Any ?: return@forEach + when(value) { + is Number -> list.add(JsonPrimitive(value)) + is Boolean -> list.add(JsonPrimitive(value)) + is String -> list.add(JsonPrimitive(value)) + is Map<*, *> -> list.add((value).toJsonElement()) + is Collection<*> -> list.add(value.toJsonElement()) + is Array<*> -> list.add(value.toList().toJsonElement()) + else -> list.add(JsonPrimitive(value.toString())) // other type } - } else if (source != null && locations != null) { - for (location in locations!!) { - output += "\n\n" + source.print(location) + } + return JsonArray(list) + } + + protected fun Map<*, *>.toJsonElement(): JsonElement { + val map: MutableMap = mutableMapOf() + this.forEach { + val key = it.key as? String ?: return@forEach + val value = it.value ?: return@forEach + when(value) { + is Number? -> map[key] = JsonPrimitive(value) + is Boolean? -> map[key] = JsonPrimitive(value) + is String? -> map[key] = JsonPrimitive(value) + is Map<*, *> -> map[key] = (value).toJsonElement() + is Collection<*> -> map[key] = value.toJsonElement() + is Array<*> -> map[key] = value.toList().toJsonElement() + else -> map[key] = JsonPrimitive(value.toString()) // other type } } + return JsonObject(map) + } - return output + protected fun buldJsonObjectByMap(it:Map): JsonObject { + return buildJsonObject { + it.forEach { (key, value) -> + when(value) { + is Number? -> put(key, value) + is String? -> put(key, value) + is Boolean? -> put(key, value) + is Map<*,*> -> put(key, value.toJsonElement()) + is Collection<*> -> put(key, value.toJsonElement()) + is Array<*> -> put(key, value.toList().toJsonElement()) + else -> put(key, JsonPrimitive(value.toString())) // other type + } + } + } } + + open fun serialize(debug:Boolean=false): String = buildJsonObject { + put("errors", buildJsonArray { + addJsonObject { + put("message", message) + put("locations", buildJsonArray { + locations?.forEach { + addJsonObject { + put("liane", it.line) + put("column", it.column) + } + } + }) + put("path", buildJsonArray { + // TODO: Build this path. https://spec.graphql.org/June2018/#example-90475 + }) + if (!debug) { + extensions?.let { + put("extensions", buldJsonObjectByMap(it)) + } + } else { + extensionsDebug().let { + put("extensions", buldJsonObjectByMap(it)) + } + } + } + }) + }.toString() } From 48be889c15e0714ecb1d8445a19d948e028b156c Mon Sep 17 00:00:00 2001 From: FFN <> Date: Mon, 6 Dec 2021 12:57:33 +0900 Subject: [PATCH 4/6] Change 1: Added extensions to the error response. Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) Change 3: Moved serialize (), which was defined as an extension of GraphQLError, into GraphQLError. option --- .../com/apurebase/kgraphql/Application.kt | 3 +- .../com/apurebase/kgraphql/GraphQLSchema.kt | 24 +++--- .../com/apurebase/kgraphql/GraphQLError.kt | 73 ++++++------------- .../kgraphql/helpers/KGraphQLExtensions.kt | 45 ++++++++++++ 4 files changed, 82 insertions(+), 63 deletions(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt index 488f7a50..481a89ad 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt @@ -23,13 +23,12 @@ fun Application.module() { install(GraphQL) { useDefaultPrettyPrinter = true playground = true - debug = true + debug = true // added option endpoint = "/" wrap { authenticate(optional = true, build = it) } - context { call -> call.authentication.principal()?.let { +it diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt index 20b35e53..7b8bb5a1 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt @@ -26,15 +26,21 @@ fun SchemaBuilder.ufoSchema() { query("testError") { description = "Returns a subset of the UFO Sighting records" resolver { -> -// try { - val ex = GraphQLError("dasdfasd","UNAUTHORIZE", mapOf("test" to arrayOf("22",23))) - val ee = ex.serialize(true) -// throw GraphQLError("dfada") -// } catch (e:Exception) { -// val stackList: Array = e.stackTrace -// throw GraphQLError("fasdfas",null,null,null,null, -// mapOf("aa" to null,"bb" to 1, "cc" to stackList, "gaga" to arrayListOf("22","ss",32,"333"), "ggg" to mapOf("aa" to "adad","bb" to arrayOf("22","ss",32,"333"), "gaga" to listOf("22","ss",32,"333")))) -// } + val ex = GraphQLError("validation error!","VALIDATION_ERROR", + mapOf("singleCheck" to + mapOf("email" to "not an email", + "age" to "Limited to 150", + ), "multiCheck" to "The 'from' number must not exceed the 'to' number")) + throw ex + User(4, "dd") + } + } + + query("testError2") { + description = "Returns a subset of the UFO Sighting records" + resolver { -> + val ex = GraphQLError("Access Token has expired","AUTHORIZATION_ERROR") + throw ex User(4, "dd") } } diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt index 9d7bf951..7c80b12a 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt @@ -1,5 +1,6 @@ package com.apurebase.kgraphql +import com.apurebase.kgraphql.helpers.toJsonElement import com.apurebase.kgraphql.schema.model.ast.ASTNode import com.apurebase.kgraphql.schema.model.ast.Location.Companion.getLocation import com.apurebase.kgraphql.schema.model.ast.Source @@ -41,12 +42,14 @@ open class GraphQLError( * 1. Added extensions To the error response.extensions type is Map? * 2. Added "error type" to make it easy to branch by error types at the client side. */ - val extensionsErrorType: String? = "INTERNAL", + val extensionsErrorType: String? = "INTERNAL_SERVER_ERROR", val extensionsErrorDetail: Map? = null ) : Exception(message) { constructor(message: String, node: ASTNode?) : this(message, nodes = node?.let(::listOf)) constructor(message: String, extensionsErrorType: String?,extensionsErrorDetail:Map?) : this(message,null,null,null,null,extensionsErrorType,extensionsErrorDetail ) + constructor(message: String, extensionsErrorType: String?) : this(message,null,null,null,null,extensionsErrorType ) + /** * An array of { line, column } locations within the source GraphQL document @@ -121,56 +124,22 @@ open class GraphQLError( return exception } - protected fun Collection<*>.toJsonElement(): JsonElement { - val list: MutableList = mutableListOf() - this.forEach { - val value = it as? Any ?: return@forEach - when(value) { - is Number -> list.add(JsonPrimitive(value)) - is Boolean -> list.add(JsonPrimitive(value)) - is String -> list.add(JsonPrimitive(value)) - is Map<*, *> -> list.add((value).toJsonElement()) - is Collection<*> -> list.add(value.toJsonElement()) - is Array<*> -> list.add(value.toList().toJsonElement()) - else -> list.add(JsonPrimitive(value.toString())) // other type - } - } - return JsonArray(list) - } - - protected fun Map<*, *>.toJsonElement(): JsonElement { - val map: MutableMap = mutableMapOf() - this.forEach { - val key = it.key as? String ?: return@forEach - val value = it.value ?: return@forEach - when(value) { - is Number? -> map[key] = JsonPrimitive(value) - is Boolean? -> map[key] = JsonPrimitive(value) - is String? -> map[key] = JsonPrimitive(value) - is Map<*, *> -> map[key] = (value).toJsonElement() - is Collection<*> -> map[key] = value.toJsonElement() - is Array<*> -> map[key] = value.toList().toJsonElement() - else -> map[key] = JsonPrimitive(value.toString()) // other type - } - } - return JsonObject(map) - } +// protected fun buldJsonObjectByMap(it:Map): JsonObject { +// return buildJsonObject { +// it.forEach { (key, value) -> +// when(value) { +// is Number? -> put(key, value) +// is String? -> put(key, value) +// is Boolean? -> put(key, value) +// is Map<*,*> -> put(key, value.toJsonElement()) +// is Collection<*> -> put(key, value.toJsonElement()) +// is Array<*> -> put(key, value.toList().toJsonElement()) +// else -> put(key, JsonPrimitive(value.toString())) // other type +// } +// } +// } +// } - protected fun buldJsonObjectByMap(it:Map): JsonObject { - return buildJsonObject { - it.forEach { (key, value) -> - when(value) { - is Number? -> put(key, value) - is String? -> put(key, value) - is Boolean? -> put(key, value) - is Map<*,*> -> put(key, value.toJsonElement()) - is Collection<*> -> put(key, value.toJsonElement()) - is Array<*> -> put(key, value.toList().toJsonElement()) - else -> put(key, JsonPrimitive(value.toString())) // other type - } - } - } - } open fun serialize(debug:Boolean=false): String = buildJsonObject { put("errors", buildJsonArray { @@ -189,11 +158,11 @@ open class GraphQLError( }) if (!debug) { extensions?.let { - put("extensions", buldJsonObjectByMap(it)) + put("extensions", it.toJsonElement()) } } else { extensionsDebug().let { - put("extensions", buldJsonObjectByMap(it)) + put("extensions", it.toJsonElement()) } } } diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/helpers/KGraphQLExtensions.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/helpers/KGraphQLExtensions.kt index 6b03cf95..91c3de13 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/helpers/KGraphQLExtensions.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/helpers/KGraphQLExtensions.kt @@ -1,6 +1,10 @@ package com.apurebase.kgraphql.helpers import com.apurebase.kgraphql.schema.execution.Execution +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive /** * This returns a list of all scalar fields requested on this type. @@ -16,3 +20,44 @@ fun Execution.getFields(): List = when (this) { } else -> listOf() }.distinct() + +/** + * Collection : Convert to JsonElement + */ +fun Collection<*>.toJsonElement(): JsonElement { + val list: MutableList = mutableListOf() + this.forEach { + val value = it as? Any ?: return@forEach + when(value) { + is Number -> list.add(JsonPrimitive(value)) + is Boolean -> list.add(JsonPrimitive(value)) + is String -> list.add(JsonPrimitive(value)) + is Map<*, *> -> list.add((value).toJsonElement()) + is Collection<*> -> list.add(value.toJsonElement()) + is Array<*> -> list.add(value.toList().toJsonElement()) + else -> list.add(JsonPrimitive(value.toString())) // other type + } + } + return JsonArray(list) +} + +/** + * Map : Convert to JsonElement + */ +fun Map<*, *>.toJsonElement(): JsonElement { + val map: MutableMap = mutableMapOf() + this.forEach { + val key = it.key as? String ?: return@forEach + val value = it.value ?: return@forEach + when(value) { + is Number? -> map[key] = JsonPrimitive(value) + is Boolean? -> map[key] = JsonPrimitive(value) + is String? -> map[key] = JsonPrimitive(value) + is Map<*, *> -> map[key] = (value).toJsonElement() + is Collection<*> -> map[key] = value.toJsonElement() + is Array<*> -> map[key] = value.toList().toJsonElement() + else -> map[key] = JsonPrimitive(value.toString()) // other type + } + } + return JsonObject(map) +} \ No newline at end of file From f850b124276052872009fd550010e49d1ae1a682 Mon Sep 17 00:00:00 2001 From: FFN <> Date: Mon, 6 Dec 2021 13:04:26 +0900 Subject: [PATCH 5/6] Change 1: Added extensions to the error response. Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) Change 3: Moved serialize (), which was defined as an extension of GraphQLError, into GraphQLError. option --- .../com/apurebase/kgraphql/Application.kt | 5 ++- .../com/apurebase/kgraphql/GraphQLSchema.kt | 26 ---------------- .../com/apurebase/kgraphql/KtorFeature.kt | 12 +------ .../com/apurebase/kgraphql/GraphQLError.kt | 31 +++---------------- 4 files changed, 10 insertions(+), 64 deletions(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt index 481a89ad..a1c913ca 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt @@ -23,7 +23,10 @@ fun Application.module() { install(GraphQL) { useDefaultPrettyPrinter = true playground = true - debug = true // added option + /** + * Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) + */ + debug = false endpoint = "/" wrap { diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt index 7b8bb5a1..1b6d94a7 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/GraphQLSchema.kt @@ -20,31 +20,6 @@ fun SchemaBuilder.ufoSchema() { deserialize = { dateString -> LocalDate.parse(dateString) } } - /** - * ima9dan Added extensions to error response json - */ - query("testError") { - description = "Returns a subset of the UFO Sighting records" - resolver { -> - val ex = GraphQLError("validation error!","VALIDATION_ERROR", - mapOf("singleCheck" to - mapOf("email" to "not an email", - "age" to "Limited to 150", - ), "multiCheck" to "The 'from' number must not exceed the 'to' number")) - throw ex - User(4, "dd") - } - } - - query("testError2") { - description = "Returns a subset of the UFO Sighting records" - resolver { -> - val ex = GraphQLError("Access Token has expired","AUTHORIZATION_ERROR") - throw ex - User(4, "dd") - } - } - query("sightings") { description = "Returns a subset of the UFO Sighting records" @@ -126,4 +101,3 @@ fun SchemaBuilder.ufoSchema() { } } } - diff --git a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt index c3b8b91a..7e01d6a0 100644 --- a/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt +++ b/kgraphql-ktor/src/main/kotlin/com/apurebase/kgraphql/KtorFeature.kt @@ -27,8 +27,7 @@ class GraphQL(val schema: Schema) { var endpoint: String = "/graphql" /** - * ima9dan - * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. + * Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) */ var debug: Boolean = false @@ -88,11 +87,6 @@ class GraphQL(val schema: Schema) { } } catch (e: Throwable) { if (e is GraphQLError) { - /** - * ima9dan - * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. - * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. - */ context.respond(HttpStatusCode.OK, e.serialize(config.debug)) } else throw e } @@ -100,9 +94,5 @@ class GraphQL(val schema: Schema) { return GraphQL(schema) } } - /** - * ima9dan : Delete serialize function because.. - * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. - */ } diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt index 7c80b12a..1d9b0301 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/GraphQLError.kt @@ -38,10 +38,8 @@ open class GraphQLError( val originalError: Throwable? = null, /** - * ima9dan - * 1. Added extensions To the error response.extensions type is Map? - * 2. Added "error type" to make it easy to branch by error types at the client side. - */ + * Change 1: Added extensions to the error response. + */ val extensionsErrorType: String? = "INTERNAL_SERVER_ERROR", val extensionsErrorDetail: Map? = null ) : Exception(message) { @@ -86,11 +84,9 @@ open class GraphQLError( } /** - * ima9dan - * 1. Added extensions To the error response.extensions type is Map? - * 2. Added "error type" to make it easy to branch by error types at the client side. - * 3. Added debug mode. If true, exception information will be output in extensions when an error occurs. - * 4. Embed serialize function to GraphQLError because we want to use GraphQLError individually. ex: at StatusPages Plugin. + * Change 1: Added extensions to the error response. + * Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) + * Change 3: Moved serialize (), which was defined as an extension of GraphQLError, into GraphQLError. option */ open val extensions: Map?by lazy { val extensions = mutableMapOf() @@ -124,23 +120,6 @@ open class GraphQLError( return exception } -// protected fun buldJsonObjectByMap(it:Map): JsonObject { -// return buildJsonObject { -// it.forEach { (key, value) -> -// when(value) { -// is Number? -> put(key, value) -// is String? -> put(key, value) -// is Boolean? -> put(key, value) -// is Map<*,*> -> put(key, value.toJsonElement()) -// is Collection<*> -> put(key, value.toJsonElement()) -// is Array<*> -> put(key, value.toList().toJsonElement()) -// else -> put(key, JsonPrimitive(value.toString())) // other type -// } -// } -// } -// } - - open fun serialize(debug:Boolean=false): String = buildJsonObject { put("errors", buildJsonArray { addJsonObject { From 89a0851b6a4e50ccec82a02c505239cb1eb1df09 Mon Sep 17 00:00:00 2001 From: FFN <> Date: Mon, 6 Dec 2021 13:48:51 +0900 Subject: [PATCH 6/6] Change 1: Added extensions to the error response. Change 2: Added debug option to GraphQL Configuration (flag to output exception information to extensions) Change 3: Moved serialize (), which was defined as an extension of GraphQLError, into GraphQLError. option --- .../src/main/kotlin/com/apurebase/kgraphql/Application.kt | 1 + kgraphql-ktor/build.gradle.kts | 1 - kgraphql/build.gradle.kts | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt index a1c913ca..f195a8ba 100644 --- a/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt +++ b/kgraphql-example/src/main/kotlin/com/apurebase/kgraphql/Application.kt @@ -32,6 +32,7 @@ fun Application.module() { wrap { authenticate(optional = true, build = it) } + context { call -> call.authentication.principal()?.let { +it diff --git a/kgraphql-ktor/build.gradle.kts b/kgraphql-ktor/build.gradle.kts index e17de0b4..5b15a615 100644 --- a/kgraphql-ktor/build.gradle.kts +++ b/kgraphql-ktor/build.gradle.kts @@ -1,6 +1,5 @@ plugins { base - id("com.github.johnrengelman.shadow") version "7.1.0" kotlin("jvm") version "1.5.10" kotlin("plugin.serialization") version "1.5.0" id("org.jetbrains.dokka") version "1.4.32" diff --git a/kgraphql/build.gradle.kts b/kgraphql/build.gradle.kts index 36d0a67a..e420407d 100644 --- a/kgraphql/build.gradle.kts +++ b/kgraphql/build.gradle.kts @@ -1,7 +1,6 @@ plugins { base - id("com.github.johnrengelman.shadow") version "7.1.0" kotlin("jvm") version "1.5.10" id("org.jetbrains.dokka") version "1.4.32" signing