Skip to content

Commit 14fb58e

Browse files
gscheibelbrennantaylor
authored andcommitted
Prevent the usage of interface as argument type (#35)
1 parent 2c0294b commit 14fb58e

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.expedia.graphql.schema.exceptions
2+
3+
/**
4+
* GraphQL Interfaces and Unions cannot be used as arguments.
5+
* Specification reference: https://facebook.github.io/graphql/draft/#sec-Field-Arguments
6+
*
7+
* This specification is translated as following:
8+
* the type of a function argument cannot be a java / kotlin interface.
9+
*
10+
* Example of invalid use cases:
11+
*
12+
* class InvalidQuery {
13+
* fun invalidInterfaceUsage (arg: AnInterface): String = arg.value
14+
*
15+
* fun invalidUnionUsage (arg: UnionMarkup): Int = arg.property
16+
* }
17+
*
18+
* interface AnInterface {
19+
* val value: String
20+
* }
21+
*
22+
* data class AnImplementation (
23+
* override val value: String = "something"
24+
* ) : AnInterface
25+
*
26+
*
27+
* interface UnionMarkup
28+
*
29+
* data class PartOfUnion( val property: Int) : UnionMarkup
30+
*/
31+
class InvalidInputFieldTypeException: RuntimeException("Object field argument cannot be an interface or a union")

src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.expedia.graphql.schema.Parameter
66
import com.expedia.graphql.schema.SchemaGeneratorConfig
77
import com.expedia.graphql.schema.exceptions.ConflictingTypesException
88
import com.expedia.graphql.schema.exceptions.CouldNotGetNameOfEnumException
9+
import com.expedia.graphql.schema.exceptions.InvalidInputFieldTypeException
910
import com.expedia.graphql.schema.exceptions.TypeNotSupportedException
1011
import com.expedia.graphql.schema.extensions.directives
1112
import com.expedia.graphql.schema.extensions.getDeprecationReason
@@ -159,11 +160,14 @@ internal class SchemaGenerator(
159160
.deprecate(prop.getDeprecationReason())
160161
.build()
161162

162-
private fun argument(parameter: KParameter): GraphQLArgument = GraphQLArgument.newArgument()
163-
.name(parameter.name)
164-
.description(parameter.graphQLDescription() ?: parameter.type.graphQLDescription())
165-
.type(graphQLTypeOf(parameter.type, true) as GraphQLInputType)
166-
.build()
163+
private fun argument(parameter: KParameter): GraphQLArgument {
164+
throwIfInterfaceIsNotAuthorized(parameter)
165+
return GraphQLArgument.newArgument()
166+
.name(parameter.name)
167+
.description(parameter.graphQLDescription() ?: parameter.type.graphQLDescription())
168+
.type(graphQLTypeOf(parameter.type, true) as GraphQLInputType)
169+
.build()
170+
}
167171

168172
private fun graphQLTypeOf(type: KType, inputType: Boolean = false): GraphQLType {
169173
val hookGraphQLType = config.hooks.willGenerateGraphQLType(type)
@@ -231,6 +235,11 @@ internal class SchemaGenerator(
231235
}
232236
}
233237

238+
@Throws(InvalidInputFieldTypeException::class)
239+
private fun throwIfInterfaceIsNotAuthorized(parameter: KParameter) {
240+
if(parameter.type.jvmErasure.java.isInterface) throw InvalidInputFieldTypeException()
241+
}
242+
234243
private fun enumType(kClass: KClass<*>): GraphQLEnumType {
235244
val enumKClass = @Suppress("UNCHECKED_CAST") (kClass as KClass<Enum<*>>)
236245
val builder = GraphQLEnumType.newEnum()

src/test/kotlin/com/expedia/graphql/schema/generator/PolymorphicTests.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.expedia.graphql.schema.generator
22

33
import com.expedia.graphql.TopLevelObjectDef
4+
import com.expedia.graphql.schema.exceptions.InvalidInputFieldTypeException
45
import com.expedia.graphql.schema.testSchemaConfig
56
import com.expedia.graphql.toSchema
67
import graphql.schema.GraphQLUnionType
@@ -38,13 +39,31 @@ class PolymorphicTests {
3839
assertEquals(1, implementationType.interfaces.size)
3940
assertEquals(implementationType.interfaces.first(), interfaceType)
4041
}
42+
43+
@Test(expected = InvalidInputFieldTypeException::class)
44+
fun `Interfaces cannot be used as input field types`() {
45+
toSchema(listOf(TopLevelObjectDef(QueryWithUnAuthorizedInterfaceArgument())), config = testSchemaConfig)
46+
}
47+
48+
@Test(expected = InvalidInputFieldTypeException::class)
49+
fun `Union cannot be used as input field types`() {
50+
toSchema(listOf(TopLevelObjectDef(QueryWithUnAuthorizedUnionArgument())), config = testSchemaConfig)
51+
}
4152
}
4253

4354
class QueryWithInterface {
4455
fun query(): AnInterface = AnImplementation()
4556
fun fromImplementation(): AnImplementation = AnImplementation()
4657
}
4758

59+
class QueryWithUnAuthorizedInterfaceArgument {
60+
fun notAllowed(arg: AnInterface): AnInterface = arg
61+
}
62+
63+
class QueryWithUnAuthorizedUnionArgument {
64+
fun notAllowed(body: BodyPart): BodyPart = body
65+
}
66+
4867
interface AnInterface {
4968
val property: String
5069
}

0 commit comments

Comments
 (0)