diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt index 32d5c4020c..51a5a2e698 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt @@ -26,6 +26,7 @@ import com.expediagroup.graphql.generator.extensions.isInterface import com.expediagroup.graphql.generator.extensions.isListType import com.expediagroup.graphql.generator.extensions.isUnion import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLArgument import kotlin.reflect.KClass import kotlin.reflect.KParameter @@ -49,7 +50,7 @@ internal fun generateArgument(generator: SchemaGenerator, parameter: KParameter) .description(parameter.getGraphQLDescription()) .type(graphQLType.safeCast()) - generateDirectives(generator, parameter).forEach { + generateDirectives(generator, parameter, DirectiveLocation.ARGUMENT_DEFINITION).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateDirective.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateDirective.kt index 6b8cbd5f7c..788f7fc829 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateDirective.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateDirective.kt @@ -21,6 +21,7 @@ import com.expediagroup.graphql.generator.extensions.getPropertyAnnotations import com.expediagroup.graphql.generator.extensions.getSimpleName import com.expediagroup.graphql.generator.extensions.getValidProperties import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLArgument import graphql.schema.GraphQLDirective import java.lang.reflect.Field @@ -29,7 +30,12 @@ import kotlin.reflect.KClass import kotlin.reflect.KProperty import com.expediagroup.graphql.annotations.GraphQLDirective as GraphQLDirectiveAnnotation -internal fun generateDirectives(generator: SchemaGenerator, element: KAnnotatedElement, parentClass: KClass<*>? = null): List { +internal fun generateDirectives( + generator: SchemaGenerator, + element: KAnnotatedElement, + location: DirectiveLocation, + parentClass: KClass<*>? = null +): List { val annotations = when { element is KProperty<*> && parentClass != null -> element.getPropertyAnnotations(parentClass) else -> element.annotations @@ -37,12 +43,14 @@ internal fun generateDirectives(generator: SchemaGenerator, element: KAnnotatedE return annotations .mapNotNull { it.getDirectiveInfo() } + .filter { it.directiveAnnotation.locations.contains(location) } .map { getDirective(generator, it) } } -internal fun generateFieldDirectives(generator: SchemaGenerator, field: Field): List = +internal fun generateEnumValueDirectives(generator: SchemaGenerator, field: Field): List = field.annotations .mapNotNull { it.getDirectiveInfo() } + .filter { it.directiveAnnotation.locations.contains(DirectiveLocation.ENUM_VALUE) } .map { getDirective(generator, it) } private fun getDirective(generator: SchemaGenerator, directiveInfo: DirectiveInfo): GraphQLDirective { diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateEnum.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateEnum.kt index 4c931db85e..9e4c4b24fe 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateEnum.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateEnum.kt @@ -23,6 +23,7 @@ import com.expediagroup.graphql.generator.extensions.getGraphQLDescription import com.expediagroup.graphql.generator.extensions.getGraphQLName import com.expediagroup.graphql.generator.extensions.getSimpleName import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLEnumType import graphql.schema.GraphQLEnumValueDefinition import kotlin.reflect.KClass @@ -33,7 +34,7 @@ internal fun generateEnum(generator: SchemaGenerator, kClass: KClass enumBuilder.name(kClass.getSimpleName()) enumBuilder.description(kClass.getGraphQLDescription()) - generateDirectives(generator, kClass).forEach { + generateDirectives(generator, kClass, DirectiveLocation.ENUM).forEach { enumBuilder.withDirective(it) } @@ -51,7 +52,7 @@ private fun getEnumValueDefinition(generator: SchemaGenerator, enum: Enum<*>, kC valueBuilder.name(name) valueBuilder.value(name) - generateFieldDirectives(generator, valueField).forEach { + generateEnumValueDirectives(generator, valueField).forEach { valueBuilder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateFunction.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateFunction.kt index a3a8ba17c9..5c4269a31b 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateFunction.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateFunction.kt @@ -24,6 +24,7 @@ import com.expediagroup.graphql.generator.extensions.getGraphQLDescription import com.expediagroup.graphql.generator.extensions.getValidArguments import com.expediagroup.graphql.generator.extensions.safeCast import com.expediagroup.graphql.generator.types.utils.getWrappedReturnType +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.FieldCoordinates import graphql.schema.GraphQLFieldDefinition import graphql.schema.GraphQLOutputType @@ -40,7 +41,7 @@ internal fun generateFunction(generator: SchemaGenerator, fn: KFunction<*>, pare builder.withDirective(deprecatedDirectiveWithReason(it)) } - generateDirectives(generator, fn).forEach { + generateDirectives(generator, fn, DirectiveLocation.FIELD_DEFINITION).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputObject.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputObject.kt index a97b963d55..cc74f53251 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputObject.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputObject.kt @@ -21,6 +21,7 @@ import com.expediagroup.graphql.generator.extensions.getGraphQLDescription import com.expediagroup.graphql.generator.extensions.getSimpleName import com.expediagroup.graphql.generator.extensions.getValidProperties import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLInputObjectType import kotlin.reflect.KClass @@ -30,7 +31,7 @@ internal fun generateInputObject(generator: SchemaGenerator, kClass: KClass<*>): builder.name(kClass.getSimpleName(isInputClass = true)) builder.description(kClass.getGraphQLDescription()) - generateDirectives(generator, kClass).forEach { + generateDirectives(generator, kClass, DirectiveLocation.INPUT_OBJECT).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputProperty.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputProperty.kt index 6c51013c1d..801ea074d5 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputProperty.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInputProperty.kt @@ -20,6 +20,7 @@ import com.expediagroup.graphql.generator.SchemaGenerator import com.expediagroup.graphql.generator.extensions.getPropertyDescription import com.expediagroup.graphql.generator.extensions.getPropertyName import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLInputObjectField import graphql.schema.GraphQLInputType import kotlin.reflect.KClass @@ -35,7 +36,7 @@ internal fun generateInputProperty(generator: SchemaGenerator, prop: KProperty<* builder.name(prop.getPropertyName(parentClass)) builder.type(graphQLInputType) - generateDirectives(generator, prop, parentClass).forEach { + generateDirectives(generator, prop, DirectiveLocation.INPUT_FIELD_DEFINITION, parentClass).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt index 3d4aa6a9f9..f92d40d7c8 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt @@ -26,6 +26,7 @@ import com.expediagroup.graphql.generator.extensions.getValidSuperclasses import com.expediagroup.graphql.generator.extensions.safeCast import com.expediagroup.graphql.generator.state.AdditionalType import graphql.TypeResolutionEnvironment +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLInterfaceType import graphql.schema.GraphQLTypeReference import kotlin.reflect.KClass @@ -37,7 +38,7 @@ internal fun generateInterface(generator: SchemaGenerator, kClass: KClass<*>): G builder.name(kClass.getSimpleName()) builder.description(kClass.getGraphQLDescription()) - generateDirectives(generator, kClass).forEach { + generateDirectives(generator, kClass, DirectiveLocation.INTERFACE).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateMutation.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateMutation.kt index a82355c6c8..97e208754a 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateMutation.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateMutation.kt @@ -21,6 +21,7 @@ import com.expediagroup.graphql.exceptions.InvalidMutationTypeException import com.expediagroup.graphql.generator.SchemaGenerator import com.expediagroup.graphql.generator.extensions.getValidFunctions import com.expediagroup.graphql.generator.extensions.isNotPublic +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLObjectType internal fun generateMutations(generator: SchemaGenerator, mutations: List): GraphQLObjectType? { @@ -37,7 +38,7 @@ internal fun generateMutations(generator: SchemaGenerator, mutations: List): Grap builder.name(name) builder.description(kClass.getGraphQLDescription()) - generateDirectives(generator, kClass).forEach { + generateDirectives(generator, kClass, DirectiveLocation.OBJECT).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateProperty.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateProperty.kt index f042b70859..3835bd5deb 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateProperty.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateProperty.kt @@ -23,6 +23,7 @@ import com.expediagroup.graphql.generator.extensions.getPropertyDescription import com.expediagroup.graphql.generator.extensions.getPropertyName import com.expediagroup.graphql.generator.extensions.getSimpleName import com.expediagroup.graphql.generator.extensions.safeCast +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.FieldCoordinates import graphql.schema.GraphQLFieldDefinition import graphql.schema.GraphQLOutputType @@ -43,7 +44,7 @@ internal fun generateProperty(generator: SchemaGenerator, prop: KProperty<*>, pa fieldBuilder.withDirective(deprecatedDirectiveWithReason(it)) } - generateDirectives(generator, prop, parentClass).forEach { + generateDirectives(generator, prop, DirectiveLocation.FIELD_DEFINITION, parentClass).forEach { fieldBuilder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateQuery.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateQuery.kt index 8ec1e84375..8e65d47929 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateQuery.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateQuery.kt @@ -21,6 +21,7 @@ import com.expediagroup.graphql.exceptions.InvalidQueryTypeException import com.expediagroup.graphql.generator.SchemaGenerator import com.expediagroup.graphql.generator.extensions.getValidFunctions import com.expediagroup.graphql.generator.extensions.isNotPublic +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLObjectType internal fun generateQueries(generator: SchemaGenerator, queries: List): GraphQLObjectType { @@ -32,7 +33,7 @@ internal fun generateQueries(generator: SchemaGenerator, queries: List): GraphQLObjectType? { @@ -38,6 +39,10 @@ internal fun generateSubscriptions(generator: SchemaGenerator, subscriptions: Li throw InvalidSubscriptionTypeException(kClass) } + generateDirectives(generator, subscription.kClass, DirectiveLocation.OBJECT).forEach { + subscriptionBuilder.withDirective(it) + } + kClass.getValidFunctions(generator.config.hooks) .forEach { if (generator.config.hooks.isValidSubscriptionReturnType(kClass, it).not()) { diff --git a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateUnion.kt b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateUnion.kt index 15e6295ea1..d4b9ca7e8b 100644 --- a/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateUnion.kt +++ b/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateUnion.kt @@ -22,6 +22,7 @@ import com.expediagroup.graphql.generator.extensions.getGraphQLDescription import com.expediagroup.graphql.generator.extensions.getSimpleName import com.expediagroup.graphql.generator.extensions.safeCast import graphql.TypeResolutionEnvironment +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLTypeReference import graphql.schema.GraphQLUnionType @@ -33,7 +34,7 @@ internal fun generateUnion(generator: SchemaGenerator, kClass: KClass<*>): Graph builder.name(kClass.getSimpleName()) builder.description(kClass.getGraphQLDescription()) - generateDirectives(generator, kClass).forEach { + generateDirectives(generator, kClass, DirectiveLocation.UNION).forEach { builder.withDirective(it) } diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateDirectiveTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateDirectiveTest.kt index 224bb7b9c5..bb7d2387d1 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateDirectiveTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateDirectiveTest.kt @@ -23,11 +23,12 @@ import com.expediagroup.graphql.generator.SchemaGenerator import com.expediagroup.graphql.generator.extensions.isTrue import com.expediagroup.graphql.getTestSchemaConfigWithMockedDirectives import com.expediagroup.graphql.test.utils.SimpleDirective -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Test +import graphql.introspection.Introspection.DirectiveLocation import kotlin.reflect.KClass import kotlin.test.assertEquals import kotlin.test.assertTrue +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test class GenerateDirectiveTest { @@ -50,12 +51,26 @@ class GenerateDirectiveTest { TWO } + @DirectiveOnInputObjectOnly + @DirectiveOnObjectOnly + @DirectiveOnFieldDefinitionOnly + data class MyExampleObject(val value: String) + @GraphQLDirective annotation class DirectiveWithEnum(val type: Type) @GraphQLDirective annotation class DirectiveWithClass(val kclass: KClass<*>) + @GraphQLDirective(locations = [DirectiveLocation.INPUT_OBJECT]) + annotation class DirectiveOnInputObjectOnly + + @GraphQLDirective(locations = [DirectiveLocation.OBJECT]) + annotation class DirectiveOnObjectOnly + + @GraphQLDirective(locations = [DirectiveLocation.FIELD_DEFINITION]) + annotation class DirectiveOnFieldDefinitionOnly + class MyClass { fun noAnnotation(string: String) = string @@ -80,6 +95,12 @@ class GenerateDirectiveTest { @DirectiveWithIgnoredArgs(string = "foo", ignoreMe = "bar") fun directiveWithIgnoredArgs(string: String) = string + + // While all of these annotations are valid kotlin code, only the ones with valid locations should be added + @DirectiveOnFieldDefinitionOnly + @DirectiveOnObjectOnly + @DirectiveOnInputObjectOnly + fun invalidDirectives(string: String) = string } data class MyClassWithConstructorArgs( @@ -92,50 +113,50 @@ class GenerateDirectiveTest { @Test fun `no annotation`() { - assertTrue(generateDirectives(basicGenerator, MyClass::noAnnotation).isEmpty().isTrue()) + assertTrue(generateDirectives(basicGenerator, MyClass::noAnnotation, DirectiveLocation.FIELD_DEFINITION).isEmpty().isTrue()) } @Test fun `no directive`() { - assertTrue(generateDirectives(basicGenerator, MyClass::noDirective).isEmpty().isTrue()) + assertTrue(generateDirectives(basicGenerator, MyClass::noDirective, DirectiveLocation.FIELD_DEFINITION).isEmpty().isTrue()) } @Test fun `has directive`() { - assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::simpleDirective).size) + assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::simpleDirective, DirectiveLocation.FIELD_DEFINITION).size) } @Test fun `has directive with string`() { - assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithString).size) + assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithString, DirectiveLocation.FIELD_DEFINITION).size) } @Test fun `has directive with enum`() { - assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithEnum).size) + assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithEnum, DirectiveLocation.FIELD_DEFINITION).size) } @Test fun `has directive with class`() { - assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithClass).size) + assertEquals(expected = 1, actual = generateDirectives(basicGenerator, MyClass::directiveWithClass, DirectiveLocation.FIELD_DEFINITION).size) } @Test fun `directives are only added to the schema once`() { val initialCount = basicGenerator.directives.size - val firstInvocation = generateDirectives(basicGenerator, MyClass::simpleDirective) + val firstInvocation = generateDirectives(basicGenerator, MyClass::simpleDirective, DirectiveLocation.FIELD_DEFINITION) assertEquals(1, firstInvocation.size) - val secondInvocation = generateDirectives(basicGenerator, MyClass::simpleDirective) + val secondInvocation = generateDirectives(basicGenerator, MyClass::simpleDirective, DirectiveLocation.FIELD_DEFINITION) assertEquals(1, secondInvocation.size) assertEquals(firstInvocation.first(), secondInvocation.first()) assertEquals(initialCount + 1, basicGenerator.directives.size) } @Test - fun `directives are valid on fields (enum values)`() { + fun `directives are valid on enum values`() { val field = Type::class.java.getField("ONE") - val directives = generateFieldDirectives(basicGenerator, field) + val directives = generateEnumValueDirectives(basicGenerator, field) assertEquals(2, directives.size) assertEquals("directiveWithString", directives.first().name) @@ -146,7 +167,7 @@ class GenerateDirectiveTest { fun `directives are empty on an enum with no valid annotations`() { val field = Type::class.java.getField("TWO") - val directives = generateFieldDirectives(basicGenerator, field) + val directives = generateEnumValueDirectives(basicGenerator, field) assertEquals(0, directives.size) } @@ -154,8 +175,8 @@ class GenerateDirectiveTest { @Test fun `directives are created per each declaration`() { val initialCount = basicGenerator.directives.size - val directivesOnFirstField = generateDirectives(basicGenerator, MyClass::directiveWithString) - val directivesOnSecondField = generateDirectives(basicGenerator, MyClass::directiveWithAnotherString) + val directivesOnFirstField = generateDirectives(basicGenerator, MyClass::directiveWithString, DirectiveLocation.FIELD_DEFINITION) + val directivesOnSecondField = generateDirectives(basicGenerator, MyClass::directiveWithAnotherString, DirectiveLocation.FIELD_DEFINITION) assertEquals(expected = 1, actual = directivesOnFirstField.size) assertEquals(expected = 1, actual = directivesOnSecondField.size) @@ -171,32 +192,47 @@ class GenerateDirectiveTest { @Test fun `directives on constructor arguments can be used with or without annotation prefix`() { - val noDirectiveResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noDirective) + val noDirectiveResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noDirective, DirectiveLocation.FIELD_DEFINITION) assertEquals(expected = 0, actual = noDirectiveResult.size) - val propertyPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::propertyPrefix) + val propertyPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::propertyPrefix, DirectiveLocation.FIELD_DEFINITION) assertEquals(expected = 1, actual = propertyPrefixResult.size) assertEquals(expected = "simpleDirective", actual = propertyPrefixResult.first().name) - val noPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noPrefix, MyClassWithConstructorArgs::class) + val noPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noPrefix, DirectiveLocation.FIELD_DEFINITION, MyClassWithConstructorArgs::class) assertEquals(expected = 1, actual = noPrefixResult.size) assertEquals(expected = "simpleDirective", actual = noPrefixResult.first().name) } @Test fun `directives on constructor arguments only works with parent class`() { - val noPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noPrefix, null) + val noPrefixResult = generateDirectives(basicGenerator, MyClassWithConstructorArgs::noPrefix, DirectiveLocation.FIELD_DEFINITION, null) assertEquals(expected = 0, actual = noPrefixResult.size) } @Test fun `exlude directive arguments @GraphQLIgnore`() { - val directives = generateDirectives(basicGenerator, MyClass::directiveWithIgnoredArgs) + val directives = generateDirectives(basicGenerator, MyClass::directiveWithIgnoredArgs, DirectiveLocation.FIELD_DEFINITION) assertEquals(expected = 1, actual = directives.size) assertEquals(expected = 1, actual = directives.first().arguments.size) assertEquals(expected = "string", actual = directives.first().arguments.first().name) } + @Test + fun `exlude directives with invalid locations`() { + val objectDirectives = generateDirectives(basicGenerator, MyExampleObject::class, DirectiveLocation.OBJECT) + assertEquals(expected = 1, actual = objectDirectives.size) + assertEquals("directiveOnObjectOnly", objectDirectives.first().name) + + val inputObjectDirectives = generateDirectives(basicGenerator, MyExampleObject::class, DirectiveLocation.INPUT_OBJECT) + assertEquals(expected = 1, actual = inputObjectDirectives.size) + assertEquals("directiveOnInputObjectOnly", inputObjectDirectives.first().name) + + val fieldDirectives = generateDirectives(basicGenerator, MyClass::invalidDirectives, DirectiveLocation.FIELD_DEFINITION) + assertEquals(expected = 1, actual = fieldDirectives.size) + assertEquals("directiveOnFieldDefinitionOnly", fieldDirectives.first().name) + } + companion object { @AfterAll fun cleanUp(generateDirectiveTest: GenerateDirectiveTest) { diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateEnumTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateEnumTest.kt index 8736a9f36a..a76e470454 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateEnumTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateEnumTest.kt @@ -19,22 +19,25 @@ package com.expediagroup.graphql.generator.types import com.expediagroup.graphql.annotations.GraphQLDescription import com.expediagroup.graphql.annotations.GraphQLName import com.expediagroup.graphql.test.utils.CustomDirective +import com.expediagroup.graphql.test.utils.InterfaceOnlyDirective import com.expediagroup.graphql.test.utils.SimpleDirective -import org.junit.jupiter.api.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue +import org.junit.jupiter.api.Test class GenerateEnumTest : TypeTestHelper() { @Suppress("Detekt.UnusedPrivateClass") @GraphQLDescription("MyTestEnum description") @SimpleDirective + @InterfaceOnlyDirective enum class MyTestEnum { @GraphQLDescription("enum 'ONE' description") @SimpleDirective + @InterfaceOnlyDirective ONE, @GraphQLDescription("enum 'TWO' description") @@ -120,8 +123,10 @@ class GenerateEnumTest : TypeTestHelper() { @Test fun `Enum values can have a multiple directives`() { val gqlEnum = assertNotNull(generateEnum(generator, MyTestEnum::class)) - assertEquals(1, gqlEnum.values.first().directives.size) - assertEquals("simpleDirective", gqlEnum.values.first().directives.first().name) + + val enumValuesDirectives = assertNotNull(gqlEnum.values.find { it.name == "ONE" }?.directives) + assertEquals(1, enumValuesDirectives.size) + assertEquals("simpleDirective", enumValuesDirectives.first().name) } @Test diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateInputObjectTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateInputObjectTest.kt index 62ebbc4595..242951865a 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateInputObjectTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateInputObjectTest.kt @@ -18,15 +18,17 @@ package com.expediagroup.graphql.generator.types import com.expediagroup.graphql.annotations.GraphQLDescription import com.expediagroup.graphql.annotations.GraphQLName +import com.expediagroup.graphql.test.utils.ObjectOnlyDirective import com.expediagroup.graphql.test.utils.SimpleDirective -import org.junit.jupiter.api.Test import kotlin.test.assertEquals +import org.junit.jupiter.api.Test -internal class GenerateInputObjectTest : TypeTestHelper() { +class GenerateInputObjectTest : TypeTestHelper() { @Suppress("Detekt.UnusedPrivateClass") @GraphQLDescription("The truth") @SimpleDirective + @ObjectOnlyDirective private class InputClass { @SimpleDirective val myField: String = "car" @@ -65,7 +67,7 @@ internal class GenerateInputObjectTest : TypeTestHelper() { } @Test - fun `directives should be on input objects`() { + fun `directives with locations as INPUT_OBJECT should only be added`() { val result = generateInputObject(generator, InputClass::class) assertEquals(1, result.directives.size) assertEquals("simpleDirective", result.directives.first().name) diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateObjectTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateObjectTest.kt index b133953cdc..ac9f3ba578 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateObjectTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateObjectTest.kt @@ -19,13 +19,14 @@ package com.expediagroup.graphql.generator.types import com.expediagroup.graphql.annotations.GraphQLDescription import com.expediagroup.graphql.annotations.GraphQLDirective import com.expediagroup.graphql.annotations.GraphQLName +import com.expediagroup.graphql.test.utils.InputObjectOnlyDirective import graphql.Scalars import graphql.introspection.Introspection import graphql.schema.GraphQLNonNull import graphql.schema.GraphQLObjectType -import org.junit.jupiter.api.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull +import org.junit.jupiter.api.Test class GenerateObjectTest : TypeTestHelper() { @@ -34,6 +35,7 @@ class GenerateObjectTest : TypeTestHelper() { @GraphQLDescription("The truth") @ObjectDirective("Don't worry") + @InputObjectOnlyDirective private class BeHappy @GraphQLName("BeHappyRenamed") @@ -67,7 +69,7 @@ class GenerateObjectTest : TypeTestHelper() { } @Test - fun `Test custom directive`() { + fun `Objects should only have directives with OBJECT in their locations`() { val result = generateObject(generator, BeHappy::class) as? GraphQLObjectType assertNotNull(result) assertEquals(1, result.directives.size) diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateQueryTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateQueryTest.kt index bed8d7f0b4..33446033bf 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateQueryTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateQueryTest.kt @@ -23,16 +23,17 @@ import com.expediagroup.graphql.exceptions.EmptyQueryTypeException import com.expediagroup.graphql.exceptions.InvalidQueryTypeException import com.expediagroup.graphql.generator.extensions.isTrue import com.expediagroup.graphql.hooks.SchemaGeneratorHooks +import com.expediagroup.graphql.test.utils.InterfaceOnlyDirective import com.expediagroup.graphql.test.utils.SimpleDirective import graphql.schema.GraphQLFieldDefinition -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows @Suppress("Detekt.NestedClassesVisibility") internal class GenerateQueryTest : TypeTestHelper() { @@ -49,6 +50,7 @@ internal class GenerateQueryTest : TypeTestHelper() { fun echo(msg: String) = msg } + @InterfaceOnlyDirective @SimpleDirective class QueryObject { @GraphQLDescription("A GraphQL query method") @@ -103,7 +105,7 @@ internal class GenerateQueryTest : TypeTestHelper() { } @Test - fun `query objects can have directives`() { + fun `query objects should only have directive with OBJECT as their location`() { val queries = listOf(TopLevelObject(QueryObject())) val result = generateQueries(generator, queries) assertEquals(expected = 1, actual = result.directives.size) diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateSubscriptionTest.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateSubscriptionTest.kt index a10f17ead6..f99e7823c1 100644 --- a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateSubscriptionTest.kt +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateSubscriptionTest.kt @@ -18,19 +18,16 @@ package com.expediagroup.graphql.generator.types import com.expediagroup.graphql.TopLevelNames import com.expediagroup.graphql.TopLevelObject +import com.expediagroup.graphql.annotations.GraphQLDirective import com.expediagroup.graphql.exceptions.EmptySubscriptionTypeException import com.expediagroup.graphql.exceptions.InvalidSubscriptionTypeException import com.expediagroup.graphql.generator.extensions.getTypeOfFirstArgument import com.expediagroup.graphql.generator.extensions.isSubclassOf import com.expediagroup.graphql.hooks.SchemaGeneratorHooks +import graphql.introspection.Introspection.DirectiveLocation import graphql.schema.GraphQLFieldDefinition import io.mockk.every import io.reactivex.rxjava3.core.Flowable -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.reactivestreams.Publisher import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KType @@ -38,8 +35,13 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.reactivestreams.Publisher -internal class GenerateSubscriptionTest : TypeTestHelper() { +class GenerateSubscriptionTest : TypeTestHelper() { @Test fun `given an empty list, it should not return a field`() { @@ -148,28 +150,45 @@ internal class GenerateSubscriptionTest : TypeTestHelper() { assertEquals(1, result?.fieldDefinitions?.size) assertNotNull(result?.fieldDefinitions?.find { it.name == "number" }) } -} -class MyPublicTestSubscription { - fun counter(): Publisher = Flowable.just(1) + @Test + fun `give a valid subscription class with directives, it should only apply the valid locations`() { + val subscriptions = listOf(TopLevelObject(MyPublicTestSubscription())) + val result = assertNotNull(generateSubscriptions(generator, subscriptions)) - fun flowabelCounter(): Flowable = Flowable.just(1) + assertEquals(1, result.directives.size) + assertEquals("mySubscriptionDirective", result.directives.first().name) + } - fun filterMe(): Publisher = Flowable.just(2) -} + @MySubscriptionDirective + @MyInterfaceDirective + class MyPublicTestSubscription { + fun counter(): Publisher = Flowable.just(1) -class MyInvalidSubscriptionClass { - @Suppress("Detekt.FunctionOnlyReturningConstant") - fun number(): Int = 1 -} + fun flowabelCounter(): Flowable = Flowable.just(1) -class MyCustomSubscriptionClass { - @Suppress("Detekt.FunctionOnlyReturningConstant") - fun number(): Flow = flowOf(1) -} + fun filterMe(): Publisher = Flowable.just(2) + } -private class MyPrivateTestSubscription { - fun counter(): Publisher = Flowable.just(3) -} + class MyInvalidSubscriptionClass { + @Suppress("Detekt.FunctionOnlyReturningConstant") + fun number(): Int = 1 + } -class MyEmptyTestSubscription + class MyCustomSubscriptionClass { + @Suppress("Detekt.FunctionOnlyReturningConstant") + fun number(): Flow = flowOf(1) + } + + private class MyPrivateTestSubscription { + fun counter(): Publisher = Flowable.just(3) + } + + class MyEmptyTestSubscription + + @GraphQLDirective(locations = [DirectiveLocation.OBJECT]) + annotation class MySubscriptionDirective + + @GraphQLDirective(locations = [DirectiveLocation.INTERFACE]) + annotation class MyInterfaceDirective +} diff --git a/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/test/utils/differentLocationDirectives.kt b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/test/utils/differentLocationDirectives.kt new file mode 100644 index 0000000000..183d7ee3a9 --- /dev/null +++ b/graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/test/utils/differentLocationDirectives.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Expedia, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expediagroup.graphql.test.utils + +import com.expediagroup.graphql.annotations.GraphQLDirective +import graphql.introspection.Introspection.DirectiveLocation + +@GraphQLDirective(locations = [DirectiveLocation.INTERFACE]) +annotation class InterfaceOnlyDirective + +@GraphQLDirective(locations = [DirectiveLocation.OBJECT]) +annotation class ObjectOnlyDirective + +@GraphQLDirective(locations = [DirectiveLocation.INPUT_OBJECT]) +annotation class InputObjectOnlyDirective