Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[generator] filter lambda properties #1366

Merged
merged 1 commit into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2022 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.generator.exceptions

import kotlin.reflect.KClass
import kotlin.reflect.KProperty

/**
* Thrown when the schema defines an object property which is a lambda.
*/
class InvalidPropertyReturnTypeException(kClass: KClass<*>, property: KProperty<*>) :
GraphQLKotlinException("The class $kClass defines $property as a lambda which is currently unsupported")
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Expedia, Inc
* Copyright 2022 Expedia, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,15 +16,18 @@

package com.expediagroup.graphql.generator.internal.filters

import com.expediagroup.graphql.generator.exceptions.InvalidPropertyReturnTypeException
import com.expediagroup.graphql.generator.internal.extensions.isGraphQLIgnored
import com.expediagroup.graphql.generator.internal.extensions.isPropertyGraphQLIgnored
import com.expediagroup.graphql.generator.internal.extensions.isPublic
import com.expediagroup.graphql.generator.internal.extensions.qualifiedName
import com.expediagroup.graphql.generator.internal.types.utils.validGraphQLNameRegex
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.typeOf

private typealias PropertyFilter = (KProperty<*>, KClass<*>) -> Boolean

Expand All @@ -34,13 +37,21 @@ private val isPropertyPublic: PropertyFilter = { prop, _ -> prop.isPublic() }
private val isPropertyNotGraphQLIgnored: PropertyFilter = { prop, parentClass -> prop.isPropertyGraphQLIgnored(parentClass).not() }
private val isNotBlacklistedType: PropertyFilter = { prop, _ -> blacklistTypes.contains(prop.returnType.qualifiedName).not() }
private val isValidPropertyName: PropertyFilter = { prop, _ -> prop.name.matches(validGraphQLNameRegex) }
@OptIn(ExperimentalStdlibApi::class)
private val isNotLambda: PropertyFilter = { prop, parentClass ->
if (prop.returnType.isSubtypeOf(typeOf<Function<*>>())) {
throw InvalidPropertyReturnTypeException(parentClass, prop)
} else {
true
}
}

private val isNotIgnoredFromSuperClass: PropertyFilter = { prop, parentClass ->
val superPropsIgnored = parentClass.supertypes
.flatMap { superType ->
superType.jvmErasure.memberProperties
.filter { superProp -> basicPropertyFilters.all { it.invoke(superProp, superType::class) } }
.filter { it.isGraphQLIgnored() }
.filter { superProp -> basicPropertyFilters.all { it.invoke(superProp, superType::class) } }
}

superPropsIgnored.none { superProp ->
Expand All @@ -49,5 +60,5 @@ private val isNotIgnoredFromSuperClass: PropertyFilter = { prop, parentClass ->
}
}

private val basicPropertyFilters = listOf(isPropertyPublic, isNotBlacklistedType)
internal val propertyFilters: List<PropertyFilter> = basicPropertyFilters + isPropertyNotGraphQLIgnored + isNotIgnoredFromSuperClass + isValidPropertyName
private val basicPropertyFilters = listOf(isPropertyPublic, isNotLambda, isNotBlacklistedType)
internal val propertyFilters: List<PropertyFilter> = listOf(isPropertyNotGraphQLIgnored) + basicPropertyFilters + isNotIgnoredFromSuperClass + isValidPropertyName
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Expedia, Inc
* Copyright 2022 Expedia, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,9 @@
package com.expediagroup.graphql.generator.internal.filters

import com.expediagroup.graphql.generator.annotations.GraphQLIgnore
import com.expediagroup.graphql.generator.exceptions.InvalidPropertyReturnTypeException
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.test.assertFalse
Expand All @@ -35,6 +37,13 @@ internal class PropertyFiltersTest {
assertFalse(isValidProperty(MyDataClass::ignoredProperty, MyDataClass::class))
assertFalse(isValidProperty(MyClass::foo, MyClass::class))
assertFalse(isValidProperty(MyClass::`$invalidPropertyName`, MyClass::class))

assertThrows<InvalidPropertyReturnTypeException> {
isValidProperty(MyClass::lambda, MyClass::class)
}
assertThrows<InvalidPropertyReturnTypeException> {
isValidProperty(MyClass::suspendableLambda, MyClass::class)
}
}

internal data class MyDataClass(
Expand All @@ -49,7 +58,10 @@ internal class PropertyFiltersTest {
val foo: Int
}

internal class MyClass : MyInterface {
internal class MyClass(
val lambda: () -> String,
val suspendableLambda: suspend () -> String
) : MyInterface {

val publicProperty: Int = 0

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Expedia, Inc
* Copyright 2022 Expedia, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,16 +18,19 @@ package com.expediagroup.graphql.generator.internal.types

import com.expediagroup.graphql.generator.annotations.GraphQLDescription
import com.expediagroup.graphql.generator.annotations.GraphQLDirective
import com.expediagroup.graphql.generator.annotations.GraphQLIgnore
import com.expediagroup.graphql.generator.annotations.GraphQLName
import com.expediagroup.graphql.generator.annotations.GraphQLValidObjectLocations
import com.expediagroup.graphql.generator.exceptions.InvalidGraphQLNameException
import com.expediagroup.graphql.generator.exceptions.InvalidObjectLocationException
import com.expediagroup.graphql.generator.exceptions.InvalidPropertyReturnTypeException
import graphql.Scalars
import graphql.introspection.Introspection
import graphql.schema.GraphQLNonNull
import graphql.schema.GraphQLObjectType
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
Expand Down Expand Up @@ -66,6 +69,20 @@ class GenerateObjectTest : TypeTestHelper() {
@GraphQLName("Invalid\$Name")
class InvalidOutputTypeNameOverride

data class FooLambda(
val foo: () -> String
)

data class SuspendableFooLambda(
val foo: suspend () -> String?
)

data class Foo(
val foo: String,
@GraphQLIgnore val fooLambda: () -> String?,
private val fooSuspendable: suspend () -> String?
)

@Test
fun `Test naming`() {
val result = generateObject(generator, BeHappy::class) as? GraphQLObjectType
Expand Down Expand Up @@ -139,4 +156,27 @@ class GenerateObjectTest : TypeTestHelper() {
generateInputObject(generator, InvalidOutputTypeNameOverride::class)
}
}

@Test
fun `Generation of output object will fail if one of the properties is lambda`() {
assertThrows<InvalidPropertyReturnTypeException> {
generateObject(generator, FooLambda::class)
}
}

@Test
fun `Generation of output object will fail if one of the properties is suspendable lambda`() {
assertThrows<InvalidPropertyReturnTypeException> {
generateObject(generator, SuspendableFooLambda::class)
}
}

@Test
fun `Generation of output objects ignores private or @GraphQLIgnored lambdas `() {
val result = generateObject(generator, Foo::class)
assertNotNull(result)
assertEquals("Foo", result.name)
assertEquals(1, result.fieldDefinitions.size)
assertEquals("foo", result.fieldDefinitions[0].name)
}
}