diff --git a/pom.xml b/pom.xml index 56dcae9b..6e62adee 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.graphql-java-kickstart graphql-java-tools - 6.0.3-SNAPSHOT + 6.1.0-SNAPSHOT jar GraphQL Java Tools diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaObjects.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaObjects.kt index 89bdd4b5..c29a46b4 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaObjects.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaObjects.kt @@ -4,7 +4,6 @@ import graphql.schema.GraphQLCodeRegistry import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLSchema import graphql.schema.GraphQLType -import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility /** * @author Andrew Potter @@ -14,11 +13,7 @@ data class SchemaObjects(val query: GraphQLObjectType, val mutation: GraphQLObje /** * Makes a GraphQLSchema with query, mutation and subscription. */ - fun toSchema(introspectionEnabled: Boolean): GraphQLSchema { - if (!introspectionEnabled) { - codeRegistryBuilder.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY) - } - + fun toSchema(): GraphQLSchema { return GraphQLSchema.newSchema() .query(query) .mutation(mutation) diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt index 8dba70ce..50f6360e 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt @@ -6,6 +6,7 @@ import graphql.schema.* import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.ScalarInfo import graphql.schema.idl.SchemaGeneratorHelper +import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility import org.slf4j.LoggerFactory import java.util.* import kotlin.reflect.KClass @@ -62,9 +63,13 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat * Parses the given schema with respect to the given dictionary and returns GraphQL objects. */ fun parseSchemaObjects(): SchemaObjects { + if (!options.introspectionEnabled) { + codeRegistryBuilder.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY) + } + // this overrides the above introspection enabled setting obviously... todo: add documentation + options.fieldVisilibity?.let { codeRegistryBuilder.fieldVisibility(it) } // Create GraphQL objects -// val inputObjects = inputObjectDefinitions.map { createInputObject(it, listOf())} val inputObjects: MutableList = mutableListOf() inputObjectDefinitions.forEach { if (inputObjects.none { io -> io.name == it.name }) { @@ -101,7 +106,7 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat /** * Parses the given schema with respect to the given dictionary and returns a GraphQLSchema */ - fun makeExecutableSchema(): GraphQLSchema = parseSchemaObjects().toSchema(options.introspectionEnabled) + fun makeExecutableSchema(): GraphQLSchema = parseSchemaObjects().toSchema() /** * Returns any unused type definitions that were found in the schema diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParserBuilder.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParserBuilder.kt index f8e2b2bd..0b725aa9 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaParserBuilder.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParserBuilder.kt @@ -10,6 +10,7 @@ import graphql.schema.DataFetchingEnvironment import graphql.schema.GraphQLScalarType import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.SchemaDirectiveWiring +import graphql.schema.visibility.GraphqlFieldVisibility import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.ReceiveChannel @@ -28,458 +29,465 @@ import kotlin.reflect.KClass */ class SchemaParserBuilder constructor(private val dictionary: SchemaParserDictionary = SchemaParserDictionary()) { - private val schemaString = StringBuilder() - private val files = mutableListOf() - private val resolvers = mutableListOf>() - private val scalars = mutableListOf() - private val runtimeWiringBuilder = RuntimeWiring.newRuntimeWiring() - private var options = SchemaParserOptions.defaultOptions() - - /** - * Add GraphQL schema files from the classpath. - */ - fun files(vararg files: String) = this.apply { - files.forEach { this.file(it) } - } - - /** - * Add a GraphQL Schema file from the classpath. - */ - fun file(filename: String) = this.apply { - files.add(filename) - } + private val schemaString = StringBuilder() + private val files = mutableListOf() + private val resolvers = mutableListOf>() + private val scalars = mutableListOf() + private val runtimeWiringBuilder = RuntimeWiring.newRuntimeWiring() + private var options = SchemaParserOptions.defaultOptions() + + /** + * Add GraphQL schema files from the classpath. + */ + fun files(vararg files: String) = this.apply { + files.forEach { this.file(it) } + } + + /** + * Add a GraphQL Schema file from the classpath. + */ + fun file(filename: String) = this.apply { + files.add(filename) + } + + /** + * Add a GraphQL schema string directly. + */ + fun schemaString(string: String) = this.apply { + if (schemaString.isNotEmpty()) { + schemaString.append("\n") + } + schemaString.append(string) + } + + /** + * Add GraphQLResolvers to the parser's dictionary. + */ + fun resolvers(vararg resolvers: GraphQLResolver<*>) = this.apply { + this.resolvers.addAll(resolvers) + } + + /** + * Add GraphQLResolvers to the parser's dictionary. + */ + fun resolvers(resolvers: List>) = this.apply { + this.resolvers.addAll(resolvers) + } + + /** + * Add scalars to the parser's dictionary. + */ + fun scalars(vararg scalars: GraphQLScalarType) = this.apply { + this.scalars.addAll(scalars) + } + + /** + * Add scalars to the parser's dictionary. + */ + fun scalars(scalars: List) = this.apply { + this.scalars.addAll(scalars) + } + + fun directive(name: String, directive: SchemaDirectiveWiring) = this.apply { + this.runtimeWiringBuilder.directive(name, directive) + } + + fun directiveWiring(directive: SchemaDirectiveWiring) = this.apply { + this.runtimeWiringBuilder.directiveWiring(directive) + } + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun dictionary(name: String, clazz: Class<*>) = this.apply { + this.dictionary.add(name, clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun dictionary(name: String, clazz: KClass<*>) = this.apply { + this.dictionary.add(name, clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun dictionary(dictionary: Map>) = this.apply { + this.dictionary.add(dictionary) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun dictionary(clazz: Class<*>) = this.apply { + this.dictionary.add(clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun dictionary(clazz: KClass<*>) = this.apply { + this.dictionary.add(clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun dictionary(vararg dictionary: Class<*>) = this.apply { + this.dictionary.add(*dictionary) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun dictionary(vararg dictionary: KClass<*>) = this.apply { + this.dictionary.add(*dictionary) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun dictionary(dictionary: Collection>) = this.apply { + this.dictionary.add(dictionary) + } + + fun options(options: SchemaParserOptions) = this.apply { + this.options = options + } + + /** + * Scan for classes with the supplied schema and dictionary. Used for testing. + */ + private fun scan(): ScannedSchemaObjects { + val definitions = appendDynamicDefinitions(parseDefinitions()) + val customScalars = scalars.associateBy { it.name } + + return SchemaClassScanner(dictionary.getDictionary(), definitions, resolvers, customScalars, options) + .scanForClasses() + } + + private fun parseDefinitions() = parseDocuments().flatMap { it.definitions } + + private fun appendDynamicDefinitions(baseDefinitions: List>): List> { + val definitions = baseDefinitions.toMutableList() + options.typeDefinitionFactories.forEach { definitions.addAll(it.create(definitions)) } + return definitions.toList() + } + + private fun parseDocuments(): List { + val parser = Parser() + val documents = mutableListOf() + try { + files.forEach { documents.add(parser.parseDocument(readFile(it), it)) } + + if (schemaString.isNotEmpty()) { + documents.add(parser.parseDocument(this.schemaString.toString())) + } + } catch (pce: ParseCancellationException) { + val cause = pce.cause + if (cause != null && cause is RecognitionException) { + throw InvalidSchemaError(pce, cause) + } else { + throw pce + } + } + return documents + } + + private fun readFile(filename: String): String { + return java.io.BufferedReader(java.io.InputStreamReader( + object : Any() {}.javaClass.classLoader.getResourceAsStream(filename) + ?: throw java.io.FileNotFoundException("classpath:$filename") + )).readText() + } + + /** + * Build the parser with the supplied schema and dictionary. + */ + fun build() = SchemaParser(scan(), options, runtimeWiringBuilder.build()) +} - /** - * Add a GraphQL schema string directly. - */ - fun schemaString(string: String) = this.apply { - if (schemaString.isNotEmpty()) { - schemaString.append("\n") - } - schemaString.append(string) - } +class InvalidSchemaError(pce: ParseCancellationException, private val recognitionException: RecognitionException) : RuntimeException(pce) { + override val message: String? + get() = "Invalid schema provided (${recognitionException.javaClass.name}) at: ${recognitionException.offendingToken}" +} - /** - * Add GraphQLResolvers to the parser's dictionary. - */ - fun resolvers(vararg resolvers: GraphQLResolver<*>) = this.apply { - this.resolvers.addAll(resolvers) - } +class SchemaParserDictionary { - /** - * Add GraphQLResolvers to the parser's dictionary. - */ - fun resolvers(resolvers: List>) = this.apply { - this.resolvers.addAll(resolvers) - } + private val dictionary: BiMap> = BiMap.create() + + fun getDictionary(): BiMap> = BiMap.unmodifiableBiMap(dictionary) + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun add(name: String, clazz: Class<*>) = this.apply { + this.dictionary.put(name, clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun add(name: String, clazz: KClass<*>) = this.apply { + this.dictionary.put(name, clazz.java) + } + + /** + * Add arbitrary classes to the parser's dictionary, overriding the generated type name. + */ + fun add(dictionary: Map>) = this.apply { + this.dictionary.putAll(dictionary) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun add(clazz: Class<*>) = this.apply { + this.add(clazz.simpleName, clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun add(clazz: KClass<*>) = this.apply { + this.add(clazz.java.simpleName, clazz) + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun add(vararg dictionary: Class<*>) = this.apply { + dictionary.forEach { this.add(it) } + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun add(vararg dictionary: KClass<*>) = this.apply { + dictionary.forEach { this.add(it) } + } + + /** + * Add arbitrary classes to the parser's dictionary. + */ + fun add(dictionary: Collection>) = this.apply { + dictionary.forEach { this.add(it) } + } +} - /** - * Add scalars to the parser's dictionary. - */ - fun scalars(vararg scalars: GraphQLScalarType) = this.apply { - this.scalars.addAll(scalars) - } +data class SchemaParserOptions internal constructor( + val contextClass: Class<*>?, + val genericWrappers: List, + val allowUnimplementedResolvers: Boolean, + val objectMapperProvider: PerFieldObjectMapperProvider, + val proxyHandlers: List, + val preferGraphQLResolver: Boolean, + val introspectionEnabled: Boolean, + val coroutineContextProvider: CoroutineContextProvider, + val typeDefinitionFactories: List, + val fieldVisilibity: GraphqlFieldVisibility? +) { + companion object { + @JvmStatic + fun newOptions() = Builder() - /** - * Add scalars to the parser's dictionary. - */ - fun scalars(scalars: List) = this.apply { - this.scalars.addAll(scalars) - } + @JvmStatic + @ExperimentalCoroutinesApi + fun defaultOptions() = Builder().build() + } - fun directive(name: String, directive: SchemaDirectiveWiring) = this.apply { - this.runtimeWiringBuilder.directive(name, directive) - } + val coroutineContext: CoroutineContext + get() = coroutineContextProvider.provide() - fun directiveWiring(directive: SchemaDirectiveWiring) = this.apply { - this.runtimeWiringBuilder.directiveWiring(directive) - } + class Builder { + private var contextClass: Class<*>? = null + private val genericWrappers: MutableList = mutableListOf() + private var useDefaultGenericWrappers = true + private var allowUnimplementedResolvers = false + private var objectMapperProvider: PerFieldObjectMapperProvider = PerFieldConfiguringObjectMapperProvider() + private val proxyHandlers: MutableList = mutableListOf(Spring4AopProxyHandler(), GuiceAopProxyHandler(), JavassistProxyHandler(), WeldProxyHandler()) + private var preferGraphQLResolver = false + private var introspectionEnabled = true + private var coroutineContextProvider: CoroutineContextProvider? = null + private var typeDefinitionFactories: MutableList = mutableListOf(RelayConnectionFactory()) + private var fieldVisibility: GraphqlFieldVisibility? = null - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun dictionary(name: String, clazz: Class<*>) = this.apply { - this.dictionary.add(name, clazz) + fun contextClass(contextClass: Class<*>) = this.apply { + this.contextClass = contextClass } - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun dictionary(name: String, clazz: KClass<*>) = this.apply { - this.dictionary.add(name, clazz) + fun contextClass(contextClass: KClass<*>) = this.apply { + this.contextClass = contextClass.java } - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun dictionary(dictionary: Map>) = this.apply { - this.dictionary.add(dictionary) + fun genericWrappers(genericWrappers: List) = this.apply { + this.genericWrappers.addAll(genericWrappers) } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun dictionary(clazz: Class<*>) = this.apply { - this.dictionary.add(clazz) + fun genericWrappers(vararg genericWrappers: GenericWrapper) = this.apply { + this.genericWrappers.addAll(genericWrappers) } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun dictionary(clazz: KClass<*>) = this.apply { - this.dictionary.add(clazz) + fun useDefaultGenericWrappers(useDefaultGenericWrappers: Boolean) = this.apply { + this.useDefaultGenericWrappers = useDefaultGenericWrappers } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun dictionary(vararg dictionary: Class<*>) = this.apply { - this.dictionary.add(*dictionary) + fun allowUnimplementedResolvers(allowUnimplementedResolvers: Boolean) = this.apply { + this.allowUnimplementedResolvers = allowUnimplementedResolvers } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun dictionary(vararg dictionary: KClass<*>) = this.apply { - this.dictionary.add(*dictionary) + fun preferGraphQLResolver(preferGraphQLResolver: Boolean) = this.apply { + this.preferGraphQLResolver = preferGraphQLResolver } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun dictionary(dictionary: Collection>) = this.apply { - this.dictionary.add(dictionary) + fun objectMapperConfigurer(objectMapperConfigurer: ObjectMapperConfigurer) = this.apply { + this.objectMapperProvider = PerFieldConfiguringObjectMapperProvider(objectMapperConfigurer) } - fun options(options: SchemaParserOptions) = this.apply { - this.options = options + fun objectMapperProvider(objectMapperProvider: PerFieldObjectMapperProvider) = this.apply { + this.objectMapperProvider = objectMapperProvider } - /** - * Scan for classes with the supplied schema and dictionary. Used for testing. - */ - private fun scan(): ScannedSchemaObjects { - val definitions = appendDynamicDefinitions(parseDefinitions()) - val customScalars = scalars.associateBy { it.name } - - return SchemaClassScanner(dictionary.getDictionary(), definitions, resolvers, customScalars, options) - .scanForClasses() + fun objectMapperConfigurer(objectMapperConfigurer: (ObjectMapper, ObjectMapperConfigurerContext) -> Unit) = this.apply { + this.objectMapperConfigurer(ObjectMapperConfigurer(objectMapperConfigurer)) } - private fun parseDefinitions() = parseDocuments().flatMap { it.definitions } - - private fun appendDynamicDefinitions(baseDefinitions: List>): List> { - val definitions = baseDefinitions.toMutableList() - options.typeDefinitionFactories.forEach { definitions.addAll(it.create(definitions)) } - return definitions.toList() + fun addProxyHandler(proxyHandler: ProxyHandler) = this.apply { + this.proxyHandlers.add(proxyHandler) } - private fun parseDocuments(): List { - val parser = Parser() - val documents = mutableListOf() - try { - files.forEach { documents.add(parser.parseDocument(readFile(it), it)) } - - if (schemaString.isNotEmpty()) { - documents.add(parser.parseDocument(this.schemaString.toString())) - } - } catch (pce: ParseCancellationException) { - val cause = pce.cause - if (cause != null && cause is RecognitionException) { - throw InvalidSchemaError(pce, cause) - } else { - throw pce - } - } - return documents + fun introspectionEnabled(introspectionEnabled: Boolean) = this.apply { + this.introspectionEnabled = introspectionEnabled } - private fun readFile(filename: String): String { - return java.io.BufferedReader(java.io.InputStreamReader( - object : Any() {}.javaClass.classLoader.getResourceAsStream(filename) - ?: throw java.io.FileNotFoundException("classpath:$filename") - )).readText() + fun coroutineContext(context: CoroutineContext) = this.apply { + this.coroutineContextProvider = DefaultCoroutineContextProvider(context) } - /** - * Build the parser with the supplied schema and dictionary. - */ - fun build() = SchemaParser(scan(), options, runtimeWiringBuilder.build()) -} - -class InvalidSchemaError(pce: ParseCancellationException, private val recognitionException: RecognitionException) : RuntimeException(pce) { - override val message: String? - get() = "Invalid schema provided (${recognitionException.javaClass.name}) at: ${recognitionException.offendingToken}" -} - -class SchemaParserDictionary { - - private val dictionary: BiMap> = BiMap.create() - - fun getDictionary(): BiMap> = BiMap.unmodifiableBiMap(dictionary) - - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun add(name: String, clazz: Class<*>) = this.apply { - this.dictionary.put(name, clazz) + fun coroutineContextProvider(contextProvider: CoroutineContextProvider) = this.apply { + this.coroutineContextProvider = contextProvider } - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun add(name: String, clazz: KClass<*>) = this.apply { - this.dictionary.put(name, clazz.java) + fun typeDefinitionFactory(factory: TypeDefinitionFactory) = this.apply { + this.typeDefinitionFactories.add(factory) } - /** - * Add arbitrary classes to the parser's dictionary, overriding the generated type name. - */ - fun add(dictionary: Map>) = this.apply { - this.dictionary.putAll(dictionary) + fun fieldVisibility(fieldVisilibity: GraphqlFieldVisibility) = this.apply { + this.fieldVisibility = fieldVisibility } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun add(clazz: Class<*>) = this.apply { - this.add(clazz.simpleName, clazz) - } + @ExperimentalCoroutinesApi + fun build(): SchemaParserOptions { + val coroutineContextProvider = coroutineContextProvider ?: DefaultCoroutineContextProvider(Dispatchers.Default) + val wrappers = if (useDefaultGenericWrappers) { + genericWrappers + listOf( + GenericWrapper(Future::class, 0), + GenericWrapper(CompletableFuture::class, 0), + GenericWrapper(CompletionStage::class, 0), + GenericWrapper(Publisher::class, 0), + GenericWrapper.withTransformer(ReceiveChannel::class, 0, { receiveChannel, environment -> + environment.coroutineScope().publish(coroutineContextProvider.provide()) { + try { + for (item in receiveChannel) { + send(item) + } + } finally { + receiveChannel.cancel() + } + } + }) + ) + } else { + genericWrappers + } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun add(clazz: KClass<*>) = this.apply { - this.add(clazz.java.simpleName, clazz) + return SchemaParserOptions(contextClass, wrappers, allowUnimplementedResolvers, objectMapperProvider, + proxyHandlers, preferGraphQLResolver, introspectionEnabled, coroutineContextProvider, + typeDefinitionFactories, fieldVisibility + ) } + } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun add(vararg dictionary: Class<*>) = this.apply { - dictionary.forEach { this.add(it) } + internal class DefaultCoroutineContextProvider(val coroutineContext: CoroutineContext) : CoroutineContextProvider { + override fun provide(): CoroutineContext { + return coroutineContext } + } - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun add(vararg dictionary: KClass<*>) = this.apply { - dictionary.forEach { this.add(it) } - } + data class GenericWrapper( + val type: Class<*>, + val index: Int, + val transformer: (Any, DataFetchingEnvironment) -> Any? = { x, _ -> x }, + val schemaWrapper: (JavaType) -> JavaType = { x -> x } + ) { - /** - * Add arbitrary classes to the parser's dictionary. - */ - fun add(dictionary: Collection>) = this.apply { - dictionary.forEach { this.add(it) } - } -} + constructor(type: Class<*>, index: Int) : this(type, index, { x, _ -> x }) + constructor(type: KClass<*>, index: Int) : this(type.java, index, { x, _ -> x }) -data class SchemaParserOptions internal constructor( - val contextClass: Class<*>?, - val genericWrappers: List, - val allowUnimplementedResolvers: Boolean, - val objectMapperProvider: PerFieldObjectMapperProvider, - val proxyHandlers: List, - val preferGraphQLResolver: Boolean, - val introspectionEnabled: Boolean, - val coroutineContextProvider: CoroutineContextProvider, - val typeDefinitionFactories: List -) { companion object { - @JvmStatic - fun newOptions() = Builder() - - @JvmStatic - @ExperimentalCoroutinesApi - fun defaultOptions() = Builder().build() - } - - val coroutineContext: CoroutineContext - get() = coroutineContextProvider.provide() - - class Builder { - private var contextClass: Class<*>? = null - private val genericWrappers: MutableList = mutableListOf() - private var useDefaultGenericWrappers = true - private var allowUnimplementedResolvers = false - private var objectMapperProvider: PerFieldObjectMapperProvider = PerFieldConfiguringObjectMapperProvider() - private val proxyHandlers: MutableList = mutableListOf(Spring4AopProxyHandler(), GuiceAopProxyHandler(), JavassistProxyHandler(), WeldProxyHandler()) - private var preferGraphQLResolver = false - private var introspectionEnabled = true - private var coroutineContextProvider: CoroutineContextProvider? = null - private var coroutineContext: CoroutineContext? = null - private var typeDefinitionFactories: MutableList = mutableListOf(RelayConnectionFactory()) - - fun contextClass(contextClass: Class<*>) = this.apply { - this.contextClass = contextClass - } - - fun contextClass(contextClass: KClass<*>) = this.apply { - this.contextClass = contextClass.java - } - - fun genericWrappers(genericWrappers: List) = this.apply { - this.genericWrappers.addAll(genericWrappers) - } - - fun genericWrappers(vararg genericWrappers: GenericWrapper) = this.apply { - this.genericWrappers.addAll(genericWrappers) - } - - fun useDefaultGenericWrappers(useDefaultGenericWrappers: Boolean) = this.apply { - this.useDefaultGenericWrappers = useDefaultGenericWrappers - } - - fun allowUnimplementedResolvers(allowUnimplementedResolvers: Boolean) = this.apply { - this.allowUnimplementedResolvers = allowUnimplementedResolvers - } - - fun preferGraphQLResolver(preferGraphQLResolver: Boolean) = this.apply { - this.preferGraphQLResolver = preferGraphQLResolver - } - - fun objectMapperConfigurer(objectMapperConfigurer: ObjectMapperConfigurer) = this.apply { - this.objectMapperProvider = PerFieldConfiguringObjectMapperProvider(objectMapperConfigurer) - } - - fun objectMapperProvider(objectMapperProvider: PerFieldObjectMapperProvider) = this.apply { - this.objectMapperProvider = objectMapperProvider - } - - fun objectMapperConfigurer(objectMapperConfigurer: (ObjectMapper, ObjectMapperConfigurerContext) -> Unit) = this.apply { - this.objectMapperConfigurer(ObjectMapperConfigurer(objectMapperConfigurer)) - } - - fun addProxyHandler(proxyHandler: ProxyHandler) = this.apply { - this.proxyHandlers.add(proxyHandler) - } - - fun introspectionEnabled(introspectionEnabled: Boolean) = this.apply { - this.introspectionEnabled = introspectionEnabled - } - - fun coroutineContext(context: CoroutineContext) = this.apply { - this.coroutineContextProvider = DefaultCoroutineContextProvider(context) - } - - fun coroutineContextProvider(contextProvider: CoroutineContextProvider) = this.apply { - this.coroutineContextProvider = contextProvider - } - - fun typeDefinitionFactory(factory: TypeDefinitionFactory) = this.apply { - this.typeDefinitionFactories.add(factory) - } - - @ExperimentalCoroutinesApi - fun build(): SchemaParserOptions { - val coroutineContextProvider = coroutineContextProvider ?: DefaultCoroutineContextProvider(Dispatchers.Default) - val wrappers = if (useDefaultGenericWrappers) { - genericWrappers + listOf( - GenericWrapper(Future::class, 0), - GenericWrapper(CompletableFuture::class, 0), - GenericWrapper(CompletionStage::class, 0), - GenericWrapper(Publisher::class, 0), - GenericWrapper.withTransformer(ReceiveChannel::class, 0, { receiveChannel, environment -> - environment.coroutineScope().publish(coroutineContextProvider.provide()) { - try { - for (item in receiveChannel) { - send(item) - } - } finally { - receiveChannel.cancel() - } - } - }) - ) - } else { - genericWrappers - } - - return SchemaParserOptions(contextClass, wrappers, allowUnimplementedResolvers, objectMapperProvider, - proxyHandlers, preferGraphQLResolver, introspectionEnabled, coroutineContextProvider, typeDefinitionFactories) - } - } - internal class DefaultCoroutineContextProvider(val coroutineContext: CoroutineContext): CoroutineContextProvider { - override fun provide(): CoroutineContext { - return coroutineContext - } - } - - data class GenericWrapper( - val type: Class<*>, - val index: Int, - val transformer: (Any, DataFetchingEnvironment) -> Any? = { x, _ -> x }, - val schemaWrapper: (JavaType) -> JavaType = { x -> x } - ) { - - constructor(type: Class<*>, index: Int) : this(type, index, { x, _ -> x }) - constructor(type: KClass<*>, index: Int) : this(type.java, index, { x, _ -> x }) - - companion object { - - @Suppress("UNCHECKED_CAST") - @JvmStatic - fun withTransformer( - type: Class, - index: Int, - transformer: (T, DataFetchingEnvironment) -> Any?, - schemaWrapper: (JavaType) -> JavaType = { x -> x } - ): GenericWrapper where T : Any { - return GenericWrapper(type, index, transformer as (Any, DataFetchingEnvironment) -> Any?, schemaWrapper) - } - - fun withTransformer( - type: KClass, - index: Int, - transformer: (T, DataFetchingEnvironment) -> Any?, - schemaWrapper: (JavaType) -> JavaType = { x -> x } - ): GenericWrapper where T : Any { - return withTransformer(type.java, index, transformer, schemaWrapper) - } - - @JvmStatic - fun withTransformer( - type: Class, - index: Int, - transformer: (T) -> Any?, - schemaWrapper: (JavaType) -> JavaType = { x -> x } - ): GenericWrapper where T : Any { - return withTransformer(type, index, { x, _ -> transformer.invoke(x) }, schemaWrapper) - } - - fun withTransformer( - type: KClass, - index: Int, - transformer: (T) -> Any?, - schemaWrapper: (JavaType) -> JavaType = { x -> x } - ): GenericWrapper where T : Any { - return withTransformer(type.java, index, transformer, schemaWrapper) - } - - @JvmStatic - fun listCollectionWithTransformer( - type: Class, - index: Int, - transformer: (T) -> Any? - ): GenericWrapper where T : Any { - return withTransformer( - type, - index, - transformer, - { innerType -> ParameterizedTypeImpl.make(List::class.java, arrayOf(innerType), null) } - ) - } - - fun listCollectionWithTransformer( - type: KClass, - index: Int, - transformer: (T) -> Any? - ): GenericWrapper where T : Any { - return listCollectionWithTransformer(type.java, index, transformer) - } - } - - } + @Suppress("UNCHECKED_CAST") + @JvmStatic + fun withTransformer( + type: Class, + index: Int, + transformer: (T, DataFetchingEnvironment) -> Any?, + schemaWrapper: (JavaType) -> JavaType = { x -> x } + ): GenericWrapper where T : Any { + return GenericWrapper(type, index, transformer as (Any, DataFetchingEnvironment) -> Any?, schemaWrapper) + } + + fun withTransformer( + type: KClass, + index: Int, + transformer: (T, DataFetchingEnvironment) -> Any?, + schemaWrapper: (JavaType) -> JavaType = { x -> x } + ): GenericWrapper where T : Any { + return withTransformer(type.java, index, transformer, schemaWrapper) + } + + @JvmStatic + fun withTransformer( + type: Class, + index: Int, + transformer: (T) -> Any?, + schemaWrapper: (JavaType) -> JavaType = { x -> x } + ): GenericWrapper where T : Any { + return withTransformer(type, index, { x, _ -> transformer.invoke(x) }, schemaWrapper) + } + + fun withTransformer( + type: KClass, + index: Int, + transformer: (T) -> Any?, + schemaWrapper: (JavaType) -> JavaType = { x -> x } + ): GenericWrapper where T : Any { + return withTransformer(type.java, index, transformer, schemaWrapper) + } + + @JvmStatic + fun listCollectionWithTransformer( + type: Class, + index: Int, + transformer: (T) -> Any? + ): GenericWrapper where T : Any { + return withTransformer( + type, + index, + transformer, + { innerType -> ParameterizedTypeImpl.make(List::class.java, arrayOf(innerType), null) } + ) + } + + fun listCollectionWithTransformer( + type: KClass, + index: Int, + transformer: (T) -> Any? + ): GenericWrapper where T : Any { + return listCollectionWithTransformer(type.java, index, transformer) + } + } + + } }