Skip to content

Commit

Permalink
Feature/support deprecation of input values (#7)
Browse files Browse the repository at this point in the history
Resolves #2
  • Loading branch information
stuebingerb authored Oct 17, 2024
2 parents 1cc2f0e + f39d3fb commit bb403bf
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -724,19 +724,21 @@ open class Parser {

/**
* InputValueDefinition :
* - Description? Name : Type DefaultValue? Directives{Const}?
* - Description? Name ArgumentsDefinition? : Type DefaultValue? Directives{Const}?
*/
private fun parseInputValueDef(): InputValueDefinitionNode {
val start = lexer.token
val description = parseDescription()
val name = parseName()
val args = parseArgumentDefs()
expectToken(COLON)
val type = parseTypeReference()
val defaultValue = expectOptionalToken(EQUALS)?.let { parseValueLiteral(true) }
val directives = parseDirectives(true)

return InputValueDefinitionNode(
description = description,
arguments = args,
name = name,
type = type,
defaultValue = defaultValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class SchemaBuilder internal constructor() {

fun <T : Any> inputType(kClass: KClass<T>, block: InputTypeDSL<T>.() -> Unit) {
val input = InputTypeDSL(kClass).apply(block)
model.addInputObject(TypeDef.Input(input.name, kClass, input.description))
model.addInputObject(input.toKQLObject())
}

inline fun <reified T : Any> inputType(noinline block: InputTypeDSL<T>.() -> Unit = {}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,33 @@ package com.apurebase.kgraphql.schema.dsl.types

import com.apurebase.kgraphql.defaultKQLTypeName
import com.apurebase.kgraphql.schema.dsl.ItemDSL
import com.apurebase.kgraphql.schema.dsl.KotlinPropertyDSL
import com.apurebase.kgraphql.schema.model.PropertyDef
import com.apurebase.kgraphql.schema.model.TypeDef
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1

class InputTypeDSL<T : Any>(val kClass: KClass<T>) : ItemDSL() {

var name = kClass.defaultKQLTypeName()

private val kotlinProperties = mutableMapOf<KProperty1<T, *>, PropertyDef.Kotlin<T, *>>()

fun <R> property(kProperty: KProperty1<T, R>, block: KotlinPropertyDSL<T, R>.() -> Unit) {
val dsl = KotlinPropertyDSL(kProperty, block)
kotlinProperties[kProperty] = dsl.toKQLProperty()
}

fun <R> KProperty1<T, R>.configure(block: KotlinPropertyDSL<T, R>.() -> Unit) {
property(this, block)
}

internal fun toKQLObject(): TypeDef.Input<T> {
return TypeDef.Input(
name = name,
kClass = kClass,
kotlinProperties = kotlinProperties.toMap(),
description = description
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.apurebase.kgraphql.schema.introspection

interface __InputValue : __Described {
import com.apurebase.kgraphql.schema.model.Depreciable

interface __InputValue : Depreciable, __Described {

val type: __Type

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.apurebase.kgraphql.schema.introspection.TypeKind
import com.apurebase.kgraphql.schema.introspection.__Directive
import com.apurebase.kgraphql.schema.introspection.__EnumValue
import com.apurebase.kgraphql.schema.introspection.__Field
import com.apurebase.kgraphql.schema.introspection.__InputValue
import com.apurebase.kgraphql.schema.introspection.__Schema
import com.apurebase.kgraphql.schema.introspection.__Type
import kotlin.reflect.KClass
Expand All @@ -23,7 +24,8 @@ data class MutableSchemaDefinition(
private val objects: ArrayList<TypeDef.Object<*>> = arrayListOf(
TypeDef.Object(__Schema::class.defaultKQLTypeName(), __Schema::class),
create__TypeDefinition(),
create__DirectiveDefinition()
create__DirectiveDefinition(),
create__FieldDefinition()
),
private val queries: ArrayList<QueryDef<*>> = arrayListOf(),
private val scalars: ArrayList<TypeDef.Scalar<*>> = arrayListOf(
Expand Down Expand Up @@ -161,12 +163,37 @@ data class MutableSchemaDefinition(
}
}

private fun create__FieldDefinition() = TypeDSL(emptyList(), __Field::class).apply {
transformation(__Field::args) { args: List<__InputValue>, includeDeprecated: Boolean? ->
if (includeDeprecated == true) {
args
} else {
args.filterNot { it.isDeprecated }
}
}
}.toKQLObject()

private fun create__TypeDefinition() = TypeDSL(emptyList(), __Type::class).apply {
transformation(__Type::fields) { fields: List<__Field>?, includeDeprecated: Boolean? ->
if (includeDeprecated == true) fields else fields?.filterNot { it.isDeprecated }
if (includeDeprecated == true) {
fields
} else {
fields?.filterNot { it.isDeprecated }
}
}
transformation(__Type::inputFields) { fields: List<__InputValue>?, includeDeprecated: Boolean? ->
if (includeDeprecated == true) {
fields
} else {
fields?.filterNot { it.isDeprecated }
}
}
transformation(__Type::enumValues) { enumValues: List<__EnumValue>?, includeDeprecated: Boolean? ->
if (includeDeprecated == true) enumValues else enumValues?.filterNot { it.isDeprecated }
if (includeDeprecated == true) {
enumValues
} else {
enumValues?.filterNot { it.isDeprecated }
}
}
}.toKQLObject()

Expand Down Expand Up @@ -200,6 +227,13 @@ private fun create__DirectiveDefinition() = TypeDSL(
}
deprecate("Use `locations`.")
}
transformation(__Directive::args) { args: List<__InputValue>, includeDeprecated: Boolean? ->
if (includeDeprecated == true) {
args
} else {
args.filterNot { it.isDeprecated }
}
}
}.toKQLObject()

private fun <T> List<T>.containsAny(vararg elements: T) = elements.any { this.contains(it) }
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface TypeDef {
class Input<T : Any>(
name: String,
override val kClass: KClass<T>,
val kotlinProperties: Map<KProperty1<T, *>, PropertyDef.Kotlin<T, *>> = emptyMap(),
description: String? = null
) : BaseKQLType(name, description), Kotlin<T>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ data class InputValueDefinitionNode(
override val loc: Location?,
val name: NameNode,
val description: StringValueNode?,
val arguments: List<InputValueDefinitionNode>?,
val type: TypeNode,
val defaultValue: ValueNode?,
val directives: List<DirectiveNode>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ class InputValue<T : Any>(

override val description: String? = valueDef.description

override val isDeprecated: Boolean = valueDef.isDeprecated

override val deprecationReason: String? = valueDef.deprecationReason

val default: T? = valueDef.defaultValue
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,12 @@ class SchemaCompilation(
inputTypeProxies[kClass] = typeProxy

val fields = if (kClass.findAnnotation<NotIntrospected>() == null) {
kClass.memberProperties.map { property -> handleKotlinInputProperty(property) }
kClass.memberProperties.map { property ->
handleKotlinInputProperty(
kProperty = property,
kqlProperty = inputObjectDef.kotlinProperties[property]
)
}
} else {
listOf()
}
Expand All @@ -366,6 +371,9 @@ class SchemaCompilation(
val inputValue = inputValues.find { it.name == name }
val kqlInput = inputValue ?: InputValueDef(kType.jvmErasure, name)
val inputType = handlePossiblyWrappedType(inputValue?.kType ?: kType, TypeCategory.INPUT)
if (kqlInput.isDeprecated && !inputType.isNullable()) {
throw SchemaException("Required arguments cannot be marked as deprecated")
}
InputValue(kqlInput, inputType)
}
}
Expand All @@ -391,9 +399,24 @@ class SchemaCompilation(
return unionType
}

private suspend fun handleKotlinInputProperty(kProperty: KProperty1<*, *>): InputValue<*> {
private suspend fun <T : Any, R> handleKotlinInputProperty(
kProperty: KProperty1<T, R>,
kqlProperty: PropertyDef.Kotlin<*, *>?
): InputValue<*> {
val type = handlePossiblyWrappedType(kProperty.returnType, TypeCategory.INPUT)
return InputValue(InputValueDef(kProperty.returnType.jvmErasure, kProperty.name), type)
val actualKqlProperty = kqlProperty ?: PropertyDef.Kotlin(kProperty)
if (actualKqlProperty.isDeprecated && !type.isNullable()) {
throw SchemaException("Required fields cannot be marked as deprecated")
}
return InputValue(
InputValueDef(
kProperty.returnType.jvmErasure,
kProperty.name,
description = actualKqlProperty.description,
isDeprecated = actualKqlProperty.isDeprecated,
deprecationReason = actualKqlProperty.deprecationReason
), type
)
}

private suspend fun <T : Any, R> handleKotlinProperty(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,18 @@ abstract class BaseSchemaTest {
name
description
locations
args {
args(includeDeprecated: true) {
...InputValue
}
}
}
}
fragment FullType on __Type {
fields(includeDeprecated: true) {
name
description
args {
args(includeDeprecated: true) {
...InputValue
}
type {
Expand All @@ -56,7 +55,7 @@ abstract class BaseSchemaTest {
isDeprecated
deprecationReason
}
inputFields {
inputFields(includeDeprecated: true) {
...InputValue
}
interfaces {
Expand All @@ -78,6 +77,8 @@ abstract class BaseSchemaTest {
description
type { ...TypeRef }
defaultValue
isDeprecated
deprecationReason
}
fragment TypeRef on __Type {
Expand Down
Loading

0 comments on commit bb403bf

Please sign in to comment.