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

Do not fail on setAccessible #614

Merged
merged 4 commits into from
Jan 12, 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
Expand Up @@ -12,6 +12,7 @@ import graphql.schema.DataFetchingEnvironment
import org.apache.commons.lang3.ClassUtils
import org.apache.commons.lang3.reflect.FieldUtils
import org.slf4j.LoggerFactory
import java.lang.reflect.AccessibleObject
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.Type
Expand Down Expand Up @@ -44,13 +45,13 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
private fun findFieldResolver(field: FieldDefinition, search: Search, scanProperties: Boolean): FieldResolver? {
val method = findResolverMethod(field, search)
if (method != null) {
return MethodFieldResolver(field, search, options, method.apply { isAccessible = true })
return MethodFieldResolver(field, search, options, method.apply(trySetAccessible(field, search.type)))
}

if (scanProperties) {
val property = findResolverProperty(field, search)
if (property != null) {
return PropertyFieldResolver(field, search, options, property.apply { isAccessible = true })
return PropertyFieldResolver(field, search, options, property.apply(trySetAccessible(field, search.type)))
}
}

Expand All @@ -61,6 +62,15 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
return null
}

private fun trySetAccessible(field: FieldDefinition, type: JavaType): AccessibleObject.() -> Unit = {
try {
isAccessible = true
} catch (e: RuntimeException) {
log.warn("Unable to make field ${type.unwrap().name}#${field.name} accessible. " +
"Be sure to provide a resolver or open the enclosing module if possible.")
}
}

private fun missingFieldResolver(field: FieldDefinition, searches: List<Search>, scanProperties: Boolean): FieldResolver {
return if (options.allowUnimplementedResolvers || options.missingResolverDataFetcher != null) {
if (options.allowUnimplementedResolvers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package graphql.kickstart.tools

import graphql.ExceptionWhileDataFetching
import graphql.GraphQL
import graphql.execution.AsyncExecutionStrategy
import graphql.schema.GraphQLSchema
import org.junit.Ignore
import org.junit.Test
import java.util.*

/**
* Reflective access to private fields in closed modules is not possible since Java 17.
* When using objects from closed modules in the schema the field resolver scanner will try to access their fields but fail.
* If no other resolver is provided that will result in an [IllegalAccessException]
*/
class InaccessibleFieldResolverTest {

@Test
@Ignore // TODO enable test after upgrading to 17
fun `private field from closed module is not accessible`() {
val schema: GraphQLSchema = SchemaParser.newParser()
.schemaString(
"""
type Query {
locale: Locale
}

type Locale {
country: String!
languageTag: String!
}
""")
.resolvers(Query())
.build()
.makeExecutableSchema()
val gql: GraphQL = GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy())
.build()

val result = gql.execute(
"""
query {
locale {
country
languageTag
}
}
"""
)

assertEquals(result.errors.size, 1)
val exceptionWhileDataFetching = result.errors[0] as ExceptionWhileDataFetching
assert(exceptionWhileDataFetching.exception is IllegalAccessException)
}

@Test
fun `private field from closed module is accessible through resolver`() {
val schema: GraphQLSchema = SchemaParser.newParser()
.schemaString(
"""
type Query {
locale: Locale
}

type Locale {
country: String!
languageTag: String!
}
""")
.resolvers(Query(), LocaleResolver())
.build()
.makeExecutableSchema()
val gql: GraphQL = GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy())
.build()

val data = assertNoGraphQlErrors(gql) {
"""
query {
locale {
country
languageTag
}
}
"""
}

assertEquals(data["locale"], mapOf("country" to "US", "languageTag" to "en-US"))
}

class Query : GraphQLQueryResolver {
fun locale(): Locale = Locale.US
}

class LocaleResolver : GraphQLResolver<Locale> {
fun languageTag(locale: Locale): String = locale.toLanguageTag()
}
}