All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.graphql.resolution.ArgumentResolver.kt Maven / Gradle / Ivy

There is a newer version: 0.26.0
Show newest version
package io.fluidsonic.raptor.graphql.internal

import io.fluidsonic.graphql.*
import io.fluidsonic.raptor.*
import io.fluidsonic.stdlib.*


internal class ArgumentResolver(
	private val factoryName: String,
) {

	private val currentContext = ThreadLocal() // TODO won't work with coroutines


	// TODO refactor
	private fun Context.resolve(name: String, transforms: List Any?>): Any? {
		val context = execution.raptorContext
			?: return null

		val gqlDefinition = argumentDefinitions.first { it.name == name }
		val argument = checkNotNull(gqlDefinition.raptorArgument)

		val expectsMaybe = argument.kotlinType.classifier == Maybe::class
		if (expectsMaybe && !argumentValues.containsKey(name))
			return Maybe.nothing

		val inputScope = object : RaptorGraphInputScope, RaptorGraphScope by context { // TODO improve

			override fun invalid(details: String?): Nothing =
				error("invalid argument ($details)") // TODO
		}

		var value = argumentValues[name]?.let { value ->
			if (expectsMaybe && value == Maybe.nothing)
				return value

			val aliasType = gqlDefinition.raptorType as? AliasGraphType
			if (aliasType != null)
				inputScope.parseAliasValue(value, parse = aliasType.convertReferencedToAlias, typeRef = gqlDefinition.type)
			else
				value
		}

		if (expectsMaybe)
			value = Maybe.of(value)

		with(inputScope) {
			for (transform in transforms)
				value = transform(value)
		}

		return value
	}


	fun resolveArgument(name: String, variableName: String, transforms: List Any?> = emptyList()): Any? {
		val context = currentContext.get()
			?: error("Variable '$variableName' is delegated to argument(\"'$name'\") and can only be accessed within '$factoryName { … }'.")

		return context.resolve(name = name, transforms = transforms)
	}


	// TODO Consolidate list handling
	private fun RaptorGraphInputScope.parseAliasValue(value: Any, parse: RaptorGraphInputScope.(input: Any) -> Any, typeRef: GTypeRef): Any =
		when (typeRef) {
			is GListTypeRef -> (value as Collection).map { element ->
				element?.let { parseAliasValue(it, parse = parse, typeRef = typeRef.elementType) }
			}

			is GNamedTypeRef -> parse(value)
			is GNonNullTypeRef -> parseAliasValue(value, parse = parse, typeRef = typeRef.nullableRef)
		}


	internal inline fun  withArguments(
		argumentValues: Map,
		argumentDefinitions: Collection,
		context: GExecutorContext,
		action: () -> Result,
	): Result {
		val previousContext = currentContext.get()

		currentContext.set(Context(
			argumentDefinitions = argumentDefinitions,
			argumentValues = argumentValues,
			execution = context
		))

		try {
			return action()
		}
		finally {
			currentContext.set(previousContext)
		}
	}


	@Suppress("ProtectedInFinal")
	protected class Context(
		val argumentDefinitions: Collection,
		val argumentValues: Map,
		val execution: GExecutorContext,
	)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy