Skip to content

Commit fe65397

Browse files
committed
Stub enums
1 parent 039d318 commit fe65397

6 files changed

Lines changed: 274 additions & 6 deletions

File tree

lib/src/main/java/graphql/nadel/definition/stubbed/NadelStubbedDefinition.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class NadelStubbedDefinition(
2424
2525
Stubbed fields are meant to allow frontend clients consume new schema elements earlier so that they can iterate faster.
2626
""${'"'}
27-
directive @stubbed on FIELD_DEFINITION | OBJECT | INPUT_OBJECT | UNION
27+
directive @stubbed on FIELD_DEFINITION | OBJECT | INPUT_OBJECT | UNION | ENUM
2828
""".trimIndent(),
2929
)
3030
}

lib/src/main/java/graphql/nadel/validation/NadelStubbedValidation.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import graphql.nadel.engine.util.isNonNull
66
import graphql.nadel.engine.util.makeFieldCoordinates
77
import graphql.nadel.engine.util.unwrapAll
88
import graphql.nadel.engine.util.whenType
9+
import graphql.schema.GraphQLEnumType
910
import graphql.schema.GraphQLFieldDefinition
1011
import graphql.schema.GraphQLFieldsContainer
1112
import graphql.schema.GraphQLObjectType
@@ -18,7 +19,7 @@ internal class NadelStubbedValidation {
1819
): NadelSchemaValidationResult {
1920
return type.overall.whenType(
2021
enumType = {
21-
throw UnsupportedOperationException("Cannot stub EnumType")
22+
validateStubbedEnumType(type, it)
2223
},
2324
inputObjectType = {
2425
throw UnsupportedOperationException("Cannot stub InputObjectType")
@@ -53,6 +54,14 @@ internal class NadelStubbedValidation {
5354
}
5455
}
5556

57+
context(NadelValidationContext)
58+
private fun validateStubbedEnumType(
59+
type: NadelServiceSchemaElement.StubbedType,
60+
enumType: GraphQLEnumType,
61+
): NadelSchemaValidationResult {
62+
return ok()
63+
}
64+
5665
context(NadelValidationContext)
5766
private fun validateStubbedUnionType(
5867
type: NadelServiceSchemaElement.StubbedType,
@@ -119,7 +128,7 @@ internal class NadelStubbedValidation {
119128
context(NadelValidationContext)
120129
private fun isOutputTypeStubbed(field: GraphQLFieldDefinition): Boolean {
121130
return field.type.unwrapAll().whenType(
122-
enumType = { false },
131+
enumType = instructionDefinitions::isStubbed,
123132
inputObjectType = { false },
124133
interfaceType = { false }, // Stubbed types cannot be part of hierarchies for now…
125134
objectType = instructionDefinitions::isStubbed,

lib/src/main/java/graphql/nadel/validation/util/NadelGetReachableTypes.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ private class NadelReferencedTypeVisitor(
114114
): Boolean {
115115
visitTypeGuard(element) { return false }
116116
val node = element.node
117-
onTypeReferenced(node.name)
117+
if (node.hasStubbedDefinition()) {
118+
onStubbedTypeReferenced(node.name)
119+
} else {
120+
onTypeReferenced(node.name)
121+
}
118122
return true
119123
}
120124

lib/src/test/kotlin/graphql/nadel/validation/NadelStubbedValidationTest.kt

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package graphql.nadel.validation
22

3-
import graphql.nadel.validation.NadelSchemaValidationError.MissingUnderlyingType
3+
import graphql.nadel.validation.NadelSchemaValidationError.MissingArgumentOnUnderlying
44
import graphql.nadel.validation.util.assertSingleOfType
55
import org.junit.jupiter.api.Test
66
import kotlin.test.assertTrue
@@ -9,7 +9,7 @@ class NadelStubbedValidationTest {
99
private val source = "$" + "source"
1010

1111
@Test
12-
fun `can stub type`() {
12+
fun `can stub object type`() {
1313
val fixture = NadelValidationTestFixture(
1414
overallSchema = mapOf(
1515
"jira" to """
@@ -926,4 +926,150 @@ class NadelStubbedValidationTest {
926926
assertTrue(error.field.name == "stub")
927927
assertTrue(error.stubbedInputType.name == "StubInput")
928928
}
929+
930+
@Test
931+
fun `can stub enum type`() {
932+
val fixture = NadelValidationTestFixture(
933+
overallSchema = mapOf(
934+
"jira" to """
935+
type Query {
936+
person(id: ID!): Person
937+
}
938+
type Person {
939+
name: String
940+
}
941+
enum Alphabet @stubbed {
942+
Abc
943+
Def
944+
}
945+
""".trimIndent(),
946+
),
947+
underlyingSchema = mapOf(
948+
"jira" to """
949+
type Query {
950+
person(id: ID!): Person
951+
}
952+
type Person {
953+
name: String
954+
}
955+
""".trimIndent(),
956+
),
957+
)
958+
959+
// When
960+
val errors = validate(fixture)
961+
962+
// Then
963+
assertTrue(errors.isEmpty())
964+
}
965+
966+
@Test
967+
fun `can reference stubbed enum type`() {
968+
val fixture = NadelValidationTestFixture(
969+
overallSchema = mapOf(
970+
"jira" to """
971+
type Query {
972+
person(id: ID!): Person
973+
}
974+
type Person {
975+
alphabet: Alphabet
976+
}
977+
enum Alphabet @stubbed {
978+
Abc
979+
Def
980+
}
981+
""".trimIndent(),
982+
),
983+
underlyingSchema = mapOf(
984+
"jira" to """
985+
type Query {
986+
person(id: ID!): Person
987+
}
988+
type Person {
989+
name: String
990+
}
991+
""".trimIndent(),
992+
),
993+
)
994+
995+
// When
996+
val errors = validate(fixture)
997+
998+
// Then
999+
assertTrue(errors.isEmpty())
1000+
}
1001+
1002+
@Test
1003+
fun `cannot use stubbed enum in non stubbed field`() {
1004+
val fixture = NadelValidationTestFixture(
1005+
overallSchema = mapOf(
1006+
"jira" to """
1007+
type Query {
1008+
person(alphabet: Alphabet): Person
1009+
}
1010+
type Person {
1011+
name: String
1012+
}
1013+
enum Alphabet @stubbed {
1014+
Abc
1015+
Def
1016+
}
1017+
""".trimIndent(),
1018+
),
1019+
underlyingSchema = mapOf(
1020+
"jira" to """
1021+
type Query {
1022+
person(id: ID!): Person
1023+
}
1024+
type Person {
1025+
name: String
1026+
}
1027+
""".trimIndent(),
1028+
),
1029+
)
1030+
1031+
// When
1032+
val errors = validate(fixture)
1033+
1034+
// Then
1035+
assertTrue(errors.isNotEmpty())
1036+
1037+
val error = errors.assertSingleOfType<MissingArgumentOnUnderlying>()
1038+
assertTrue(error.overallField.name == "person")
1039+
assertTrue(error.argument.name == "alphabet")
1040+
}
1041+
1042+
@Test
1043+
fun `can use stubbed enum in stubbed type`() {
1044+
val fixture = NadelValidationTestFixture(
1045+
overallSchema = mapOf(
1046+
"jira" to """
1047+
type Query {
1048+
person: Person
1049+
}
1050+
type Person @stubbed {
1051+
id: ID!
1052+
alphabet: Alphabet
1053+
}
1054+
enum Alphabet @stubbed {
1055+
Abc
1056+
Def
1057+
}
1058+
""".trimIndent(),
1059+
),
1060+
underlyingSchema = mapOf(
1061+
"jira" to """
1062+
type Query {
1063+
echo: String
1064+
}
1065+
""".trimIndent(),
1066+
),
1067+
)
1068+
1069+
// When
1070+
val errors = validate(fixture)
1071+
1072+
// Then
1073+
assertTrue(errors.isEmpty())
1074+
}
9291075
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package graphql.nadel.tests.next.fixtures.stub
2+
3+
import graphql.nadel.tests.next.NadelIntegrationTest
4+
import java.lang.UnsupportedOperationException
5+
6+
class StubEnumTypeTest : NadelIntegrationTest(
7+
query = """
8+
{
9+
thing
10+
}
11+
""".trimIndent(),
12+
services = listOf(
13+
Service(
14+
name = "myService",
15+
overallSchema = """
16+
type Query {
17+
thing: Thing
18+
}
19+
enum Thing @stubbed {
20+
THING1
21+
THING2
22+
}
23+
""".trimIndent(),
24+
underlyingSchema = """
25+
type Query {
26+
echo: String
27+
}
28+
""".trimIndent(),
29+
runtimeWiring = { wiring ->
30+
data class Issue(
31+
val id: String,
32+
val title: String,
33+
val description: String,
34+
)
35+
wiring
36+
.type("Query") { type ->
37+
type
38+
.dataFetcher("thing") { env ->
39+
throw UnsupportedOperationException()
40+
}
41+
}
42+
},
43+
),
44+
),
45+
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// @formatter:off
2+
package graphql.nadel.tests.next.fixtures.stub
3+
4+
import graphql.nadel.tests.next.ExpectedNadelResult
5+
import graphql.nadel.tests.next.ExpectedServiceCall
6+
import graphql.nadel.tests.next.TestSnapshot
7+
import graphql.nadel.tests.next.listOfJsonStrings
8+
import kotlin.Suppress
9+
import kotlin.collections.List
10+
import kotlin.collections.listOf
11+
12+
private suspend fun main() {
13+
graphql.nadel.tests.next.update<StubEnumTypeTest>()
14+
}
15+
16+
/**
17+
* This class is generated. Do NOT modify.
18+
*
19+
* Refer to [graphql.nadel.tests.next.UpdateTestSnapshots]
20+
*/
21+
@Suppress("unused")
22+
public class StubEnumTypeTestSnapshot : TestSnapshot() {
23+
override val calls: List<ExpectedServiceCall> = listOf(
24+
ExpectedServiceCall(
25+
service = "myService",
26+
query = """
27+
| {
28+
| __typename__stubbed__thing: __typename
29+
| }
30+
""".trimMargin(),
31+
variables = "{}",
32+
result = """
33+
| {
34+
| "data": {
35+
| "__typename__stubbed__thing": "Query"
36+
| }
37+
| }
38+
""".trimMargin(),
39+
delayedResults = listOfJsonStrings(
40+
),
41+
),
42+
)
43+
44+
/**
45+
* ```json
46+
* {
47+
* "data": {
48+
* "thing": null
49+
* }
50+
* }
51+
* ```
52+
*/
53+
override val result: ExpectedNadelResult = ExpectedNadelResult(
54+
result = """
55+
| {
56+
| "data": {
57+
| "thing": null
58+
| }
59+
| }
60+
""".trimMargin(),
61+
delayedResults = listOfJsonStrings(
62+
),
63+
)
64+
}

0 commit comments

Comments
 (0)