Skip to content

Commit e98e590

Browse files
authored
Upgrade library versions and unit tests (#378)
* Upgrade library versions and unit tests One last bump of unit test coverage and update our dependencies before the 1.0.0 release * Update other versions * Fix build for jdk8 * Update coroutines version
1 parent 7f2214c commit e98e590

File tree

15 files changed

+598
-141
lines changed

15 files changed

+598
-141
lines changed

graphql-kotlin-federation/src/main/kotlin/com/expediagroup/graphql/federation/FederatedSchemaValidator.kt

Lines changed: 4 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616

1717
package com.expediagroup.graphql.federation
1818

19-
import com.expediagroup.graphql.federation.directives.FieldSet
2019
import com.expediagroup.graphql.federation.exception.InvalidFederatedSchema
20+
import com.expediagroup.graphql.federation.extensions.isFederatedType
21+
import com.expediagroup.graphql.federation.validation.validateDirective
22+
import com.expediagroup.graphql.federation.validation.validateProvidesDirective
23+
import com.expediagroup.graphql.federation.validation.validateRequiresDirective
2124
import graphql.schema.GraphQLDirective
22-
import graphql.schema.GraphQLDirectiveContainer
2325
import graphql.schema.GraphQLFieldDefinition
2426
import graphql.schema.GraphQLInterfaceType
25-
import graphql.schema.GraphQLList
2627
import graphql.schema.GraphQLObjectType
2728
import graphql.schema.GraphQLType
2829
import graphql.schema.GraphQLTypeUtil
29-
import graphql.schema.GraphQLUnionType
3030

3131
/**
3232
* Validates generated federated objects.
@@ -87,122 +87,4 @@ class FederatedSchemaValidator {
8787
throw InvalidFederatedSchema(errors)
8888
}
8989
}
90-
91-
// [OK] @requires references valid fields marked @external
92-
// [ERROR] @requires specified on base type
93-
// [ERROR] @requires specifies non-existent fields
94-
private fun validateRequiresDirective(validatedType: String, validatedField: GraphQLFieldDefinition, fieldMap: Map<String, GraphQLFieldDefinition>, extendedType: Boolean): List<String> {
95-
val errors = mutableListOf<String>()
96-
if (extendedType) {
97-
errors.addAll(validateDirective("$validatedType.${validatedField.name}", "requires", validatedField.directivesByName, fieldMap, extendedType))
98-
} else {
99-
errors.add("base $validatedType type has fields marked with @requires directive, validatedField=${validatedField.name}")
100-
}
101-
return errors
102-
}
103-
104-
// [OK] @provides on base type references valid @external fields on @extend object
105-
// [ERROR] @provides on base type references local object fields
106-
// [ERROR] @provides on base type references local fields on @extends object
107-
// [ERROR] @provides references interface type
108-
// [OK] @provides references list of valid @extend objects
109-
// [ERROR] @provides references @external list field
110-
// [ERROR] @provides references @external interface field
111-
private fun validateProvidesDirective(federatedType: String, field: GraphQLFieldDefinition): List<String> {
112-
val errors = mutableListOf<String>()
113-
val returnType = GraphQLTypeUtil.unwrapAll(field.type)
114-
if (returnType is GraphQLObjectType) {
115-
if (!returnType.isExtendedType()) {
116-
errors.add("@provides directive is specified on a $federatedType.${field.name} field references local object")
117-
} else {
118-
val returnTypeFields = returnType.fieldDefinitions.associateBy { it.name }
119-
// @provides is applicable on both base and federated types and always references @external fields
120-
errors.addAll(
121-
validateDirective(
122-
"$federatedType.${field.name}",
123-
"provides",
124-
field.directivesByName,
125-
returnTypeFields,
126-
true))
127-
}
128-
} else {
129-
errors.add("@provides directive is specified on a $federatedType.${field.name} field but it does not return an object type")
130-
}
131-
return errors
132-
}
133-
134-
private fun validateDirective(
135-
validatedType: String,
136-
targetDirective: String,
137-
directives: Map<String, GraphQLDirective>,
138-
fieldMap: Map<String, GraphQLFieldDefinition>,
139-
extendedType: Boolean
140-
): List<String> {
141-
val validationErrors = mutableListOf<String>()
142-
val directive = directives[targetDirective]
143-
144-
if (directive == null) {
145-
validationErrors.add("@$targetDirective directive is missing on federated $validatedType type")
146-
} else {
147-
val fieldSetValue = (directive.getArgument("fields")?.value as? FieldSet)?.value
148-
val fieldSet = fieldSetValue?.split(" ")?.filter { it.isNotEmpty() }.orEmpty()
149-
if (fieldSet.isEmpty()) {
150-
validationErrors.add("@$targetDirective directive on $validatedType is missing field information")
151-
} else {
152-
// validate key field set
153-
val validatedDirectiveInfo = "@$targetDirective(fields = $fieldSetValue) directive on $validatedType"
154-
validateFieldSelection(validatedDirectiveInfo, fieldSet.iterator(), fieldMap, extendedType, validationErrors)
155-
}
156-
}
157-
return validationErrors
158-
}
159-
160-
private fun validateFieldSelection(
161-
validatedDirective: String,
162-
iterator: Iterator<String>,
163-
fields: Map<String, GraphQLFieldDefinition>,
164-
extendedType: Boolean,
165-
errors: MutableList<String>
166-
) {
167-
var previousField: String? = null
168-
while (iterator.hasNext()) {
169-
val currentField = iterator.next()
170-
when (currentField) {
171-
"{" -> {
172-
val targetField = fields[previousField]?.type
173-
when (val unwrappedType = GraphQLTypeUtil.unwrapAll(targetField)) {
174-
is GraphQLInterfaceType -> validateFieldSelection(validatedDirective, iterator, unwrappedType.fieldDefinitions.associateBy { it.name }, extendedType, errors)
175-
is GraphQLObjectType -> validateFieldSelection(validatedDirective, iterator, unwrappedType.fieldDefinitions.associateBy { it.name }, extendedType, errors)
176-
else -> errors.add("$validatedDirective specifies invalid field set - field set defines nested selection set on unsupported type")
177-
}
178-
}
179-
"}" -> return
180-
else -> validateKeySetField(fields[currentField], extendedType, errors, validatedDirective)
181-
}
182-
previousField = currentField
183-
}
184-
}
185-
186-
private fun validateKeySetField(targetField: GraphQLFieldDefinition?, extendedType: Boolean, errors: MutableList<String>, validatedDirective: String) {
187-
if (null != targetField) {
188-
val externalField = targetField.getDirective("external") != null
189-
if (extendedType && !externalField) {
190-
errors.add("$validatedDirective specifies invalid field set - extended type incorrectly references local field=${targetField.name}")
191-
} else if (!extendedType && externalField) {
192-
errors.add("$validatedDirective specifies invalid field set - type incorrectly references external field=${targetField.name}")
193-
}
194-
195-
when (GraphQLTypeUtil.unwrapNonNull(targetField.type)) {
196-
is GraphQLList -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLList, field=${targetField.name}")
197-
is GraphQLInterfaceType -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLInterfaceType, field=${targetField.name}")
198-
is GraphQLUnionType -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLUnionType, field=${targetField.name}")
199-
}
200-
} else {
201-
errors.add("$validatedDirective specifies invalid field set - field set specifies fields that do not exist")
202-
}
203-
}
204-
205-
private fun GraphQLDirectiveContainer.isFederatedType() = this.getDirective("key") != null || isExtendedType()
206-
207-
private fun GraphQLDirectiveContainer.isExtendedType() = this.getDirective("extends") != null
20890
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2019 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.federation.extensions
18+
19+
import graphql.schema.GraphQLDirectiveContainer
20+
21+
internal fun GraphQLDirectiveContainer.isFederatedType() = this.getDirective("key") != null || isExtendedType()
22+
23+
internal fun GraphQLDirectiveContainer.isExtendedType() = this.getDirective("extends") != null

graphql-kotlin-federation/src/main/kotlin/com/expediagroup/graphql/federation/types/_Any.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ private object AnyCoercing : Coercing<Any, Any> {
6262
is ObjectValue -> input.objectFields
6363
.stream()
6464
.collect(Collectors.toMap(ObjectField::getName) { parseLiteral(it.value) })
65-
else -> Assert.assertShouldNeverHappen<Any>()
65+
else -> Assert.assertShouldNeverHappen()
6666
}
6767
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2019 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.federation.validation
18+
19+
import com.expediagroup.graphql.federation.directives.FieldSet
20+
import graphql.schema.GraphQLDirective
21+
import graphql.schema.GraphQLFieldDefinition
22+
23+
internal fun validateDirective(
24+
validatedType: String,
25+
targetDirective: String,
26+
directives: Map<String, GraphQLDirective>,
27+
fieldMap: Map<String, GraphQLFieldDefinition>,
28+
extendedType: Boolean
29+
): List<String> {
30+
val validationErrors = mutableListOf<String>()
31+
val directive = directives[targetDirective]
32+
33+
if (directive == null) {
34+
validationErrors.add("@$targetDirective directive is missing on federated $validatedType type")
35+
} else {
36+
val fieldSetValue = (directive.getArgument("fields")?.value as? FieldSet)?.value
37+
val fieldSet = fieldSetValue?.split(" ")?.filter { it.isNotEmpty() }.orEmpty()
38+
if (fieldSet.isEmpty()) {
39+
validationErrors.add("@$targetDirective directive on $validatedType is missing field information")
40+
} else {
41+
// validate key field set
42+
val validatedDirectiveInfo = "@$targetDirective(fields = $fieldSetValue) directive on $validatedType"
43+
validateFieldSelection(validatedDirectiveInfo, fieldSet.iterator(), fieldMap, extendedType, validationErrors)
44+
}
45+
}
46+
return validationErrors
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2019 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.federation.validation
18+
19+
import graphql.schema.GraphQLFieldDefinition
20+
import graphql.schema.GraphQLInterfaceType
21+
import graphql.schema.GraphQLObjectType
22+
import graphql.schema.GraphQLTypeUtil
23+
24+
internal fun validateFieldSelection(
25+
validatedDirective: String,
26+
iterator: Iterator<String>,
27+
fields: Map<String, GraphQLFieldDefinition>,
28+
extendedType: Boolean,
29+
errors: MutableList<String>
30+
) {
31+
var previousField: String? = null
32+
while (iterator.hasNext()) {
33+
val currentField = iterator.next()
34+
when (currentField) {
35+
"{" -> {
36+
val targetField = fields[previousField]?.type
37+
when (val unwrappedType = GraphQLTypeUtil.unwrapAll(targetField)) {
38+
is GraphQLInterfaceType -> validateFieldSelection(validatedDirective, iterator, unwrappedType.fieldDefinitions.associateBy { it.name }, extendedType, errors)
39+
is GraphQLObjectType -> validateFieldSelection(validatedDirective, iterator, unwrappedType.fieldDefinitions.associateBy { it.name }, extendedType, errors)
40+
else -> errors.add("$validatedDirective specifies invalid field set - field set defines nested selection set on unsupported type")
41+
}
42+
}
43+
"}" -> return
44+
else -> validateKeySetField(fields[currentField], extendedType, errors, validatedDirective)
45+
}
46+
previousField = currentField
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2019 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.federation.validation
18+
19+
import graphql.schema.GraphQLFieldDefinition
20+
import graphql.schema.GraphQLInterfaceType
21+
import graphql.schema.GraphQLList
22+
import graphql.schema.GraphQLTypeUtil
23+
import graphql.schema.GraphQLUnionType
24+
25+
internal fun validateKeySetField(targetField: GraphQLFieldDefinition?, extendedType: Boolean, errors: MutableList<String>, validatedDirective: String) {
26+
if (null != targetField) {
27+
val externalField = targetField.getDirective("external") != null
28+
if (extendedType && !externalField) {
29+
errors.add("$validatedDirective specifies invalid field set - extended type incorrectly references local field=${targetField.name}")
30+
} else if (!extendedType && externalField) {
31+
errors.add("$validatedDirective specifies invalid field set - type incorrectly references external field=${targetField.name}")
32+
}
33+
34+
when (GraphQLTypeUtil.unwrapNonNull(targetField.type)) {
35+
is GraphQLList -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLList, field=${targetField.name}")
36+
is GraphQLInterfaceType -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLInterfaceType, field=${targetField.name}")
37+
is GraphQLUnionType -> errors.add("$validatedDirective specifies invalid field set - field set references GraphQLUnionType, field=${targetField.name}")
38+
}
39+
} else {
40+
errors.add("$validatedDirective specifies invalid field set - field set specifies fields that do not exist")
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2019 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.federation.validation
18+
19+
import com.expediagroup.graphql.federation.extensions.isExtendedType
20+
import graphql.schema.GraphQLFieldDefinition
21+
import graphql.schema.GraphQLObjectType
22+
import graphql.schema.GraphQLTypeUtil
23+
24+
// [OK] @provides on base type references valid @external fields on @extend object
25+
// [ERROR] @provides on base type references local object fields
26+
// [ERROR] @provides on base type references local fields on @extends object
27+
// [ERROR] @provides references interface type
28+
// [OK] @provides references list of valid @extend objects
29+
// [ERROR] @provides references @external list field
30+
// [ERROR] @provides references @external interface field
31+
internal fun validateProvidesDirective(federatedType: String, field: GraphQLFieldDefinition): List<String> {
32+
val errors = mutableListOf<String>()
33+
val returnType = GraphQLTypeUtil.unwrapAll(field.type)
34+
if (returnType is GraphQLObjectType) {
35+
if (!returnType.isExtendedType()) {
36+
errors.add("@provides directive is specified on a $federatedType.${field.name} field references local object")
37+
} else {
38+
val returnTypeFields = returnType.fieldDefinitions.associateBy { it.name }
39+
// @provides is applicable on both base and federated types and always references @external fields
40+
errors.addAll(
41+
validateDirective(
42+
"$federatedType.${field.name}",
43+
"provides",
44+
field.directivesByName,
45+
returnTypeFields,
46+
true))
47+
}
48+
} else {
49+
errors.add("@provides directive is specified on a $federatedType.${field.name} field but it does not return an object type")
50+
}
51+
return errors
52+
}

0 commit comments

Comments
 (0)