Skip to content

Commit 76c9251

Browse files
committed
Fix bugs.
1 parent adc0dad commit 76c9251

File tree

11 files changed

+221
-16
lines changed

11 files changed

+221
-16
lines changed

Sources/GraphQL/Type/Definition.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,10 +1477,10 @@ extension GraphQLNonNull : Hashable {
14771477
}
14781478

14791479
/**
1480-
* A special type to allow a object/interface types to reference itself. It's replaced with the real type
1480+
* A special type to allow object/interface types to reference itself. It's replaced with the real type
14811481
* object when the schema is built.
14821482
*/
1483-
public final class GraphQLTypeReference : GraphQLType, GraphQLOutputType, GraphQLNullableType {
1483+
public final class GraphQLTypeReference : GraphQLType, GraphQLOutputType, GraphQLNullableType, GraphQLNamedType {
14841484
public let name: String
14851485
public let kind: TypeKind = .typeReference
14861486

Sources/GraphQL/Type/Directives.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public let GraphQLSkipDirective = try! GraphQLDirective(
9090
/**
9191
* Constant string used for default reason for a deprecation.
9292
*/
93-
let defaulDeprecationReason: Map = .string("\"No longer supported\"")
93+
let defaulDeprecationReason: Map = "No longer supported"
9494

9595
/**
9696
* Used to declare element of a GraphQL schema as deprecated.
@@ -104,8 +104,8 @@ public let GraphQLDeprecatedDirective = try! GraphQLDirective(
104104
.enumValue,
105105
],
106106
args: [
107-
"if": GraphQLArgument(
108-
type: GraphQLNonNull(GraphQLBoolean),
107+
"reason": GraphQLArgument(
108+
type: GraphQLString,
109109
description:
110110
"Explains why this element was deprecated, usually also including a " +
111111
"suggestion for how to access supported similar data. Formatted " +

Sources/GraphQL/Type/Introspection.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,6 @@ let __InputValue = try! GraphQLObjectType(
355355
return nil
356356
}
357357

358-
// This `print` is from the AST printer implementation
359-
// return print(astFromValue(value: defaultValue, type: inputValue.type))
360358
return defaultValue
361359
}
362360
)

Sources/GraphQL/Type/Schema.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public final class GraphQLSchema {
9292
}
9393
}
9494

95-
self.implementations = implementations
95+
self.implementations = implementations.mapValues { $0.sorted { $0.fields.count > $1.fields.count } }
9696

9797
// Enforce correct interface implementations.
9898
for (_, type) in typeMap {
@@ -117,8 +117,7 @@ public final class GraphQLSchema {
117117
return implementations[interface.name] ?? []
118118
}
119119

120-
// Should be impossible. Only UnionType and InterfaceType should conform to AbstractType
121-
return []
120+
fatalError("Should be impossible. Only UnionType and InterfaceType should conform to AbstractType")
122121
}
123122

124123
public func isPossibleType(abstractType: GraphQLAbstractType, possibleType: GraphQLObjectType) throws -> Bool {
@@ -178,8 +177,8 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
178177
return typeMap // Should never happen
179178
}
180179

181-
guard typeMap[type.name] == nil else {
182-
guard typeMap[type.name]! == type else {
180+
guard typeMap[type.name] == nil || typeMap[type.name] is GraphQLTypeReference else {
181+
guard typeMap[type.name]! == type || type is GraphQLTypeReference else {
183182
throw GraphQLError(
184183
message:
185184
"Schema must contain unique named types but contains multiple " +

Sources/GraphQL/Utilities/ASTFromValue.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func astFromValue(
3030
return nil
3131
}
3232

33-
// Convert JavaScript array to GraphQL list. If the GraphQLType is a list, but
33+
// Convert array to GraphQL list. If the GraphQLType is a list, but
3434
// the value is not an array, convert the value using the list's item type.
3535
if let type = type as? GraphQLList {
3636
let itemType = type.ofType as! GraphQLInputType
@@ -86,7 +86,7 @@ func astFromValue(
8686
return nil
8787
}
8888

89-
// Others serialize based on their corresponding JavaScript scalar types.
89+
// Others serialize based on their corresponding scalar types.
9090
if case let .number(number) = serialized {
9191
switch number.storageType {
9292
case .bool:

Sources/GraphQL/Utilities/TypeComparators.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ func == (lhs: GraphQLType, rhs: GraphQLType) -> Bool {
5555
if let r = rhs as? GraphQLNonNull {
5656
return l == r
5757
}
58+
case let l as GraphQLTypeReference:
59+
if let r = rhs as? GraphQLTypeReference {
60+
return l.name == r.name
61+
}
5862
default:
5963
return false
6064
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Foundation
2+
3+
func undefinedArgumentMessage(
4+
fieldName: String,
5+
type: String,
6+
argumentName: String,
7+
suggestedArgumentNames: [String]
8+
) -> String {
9+
var message = "Field \"\(fieldName)\" on type \"\(type)\" does not have argument \"\(argumentName)\"."
10+
11+
if !suggestedArgumentNames.isEmpty {
12+
let suggestions = quotedOrList(items: suggestedArgumentNames)
13+
message += " Did you mean \(suggestions)?"
14+
}
15+
16+
return message
17+
}
18+
19+
func KnownArgumentNames(context: ValidationContext) -> Visitor {
20+
return Visitor(
21+
enter: { node, key, parent, path, ancestors in
22+
if let node = node as? Argument, context.argument == nil, let field = context.fieldDef, let type = context.parentType {
23+
let argumentName = node.name.value
24+
let suggestedArgumentNames = getSuggestedArgumentNames(schema: context.schema, field: field, argumentName: argumentName)
25+
26+
context.report(error: GraphQLError(
27+
message: undefinedArgumentMessage(
28+
fieldName: field.name,
29+
type: type.name,
30+
argumentName: argumentName,
31+
suggestedArgumentNames: suggestedArgumentNames
32+
),
33+
nodes: [node]
34+
))
35+
}
36+
37+
return .continue
38+
}
39+
)
40+
}
41+
42+
func getSuggestedArgumentNames(
43+
schema: GraphQLSchema,
44+
field: GraphQLFieldDefinition,
45+
argumentName: String
46+
) -> [String] {
47+
return suggestionList(
48+
input: argumentName,
49+
options: field.args.map { $0.name }
50+
)
51+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Foundation
2+
3+
func missingArgumentsMessage(
4+
fieldName: String,
5+
type: String,
6+
missingArguments: [String]
7+
) -> String {
8+
let arguments = quotedOrList(items: missingArguments)
9+
return "Field \"\(fieldName)\" on type \"\(type)\" is missing required arguments \(arguments)."
10+
}
11+
12+
func ProvidedNonNullArguments(context: ValidationContext) -> Visitor {
13+
return Visitor(
14+
leave: { node, key, parent, path, ancestors in
15+
if let node = node as? Field, let field = context.fieldDef, let type = context.parentType {
16+
let requiredArguments = Set(
17+
field
18+
.args
19+
.filter { $0.type is GraphQLNonNull && $0.defaultValue == nil }
20+
.map { $0.name }
21+
)
22+
23+
let providedArguments = Set(node.arguments.map { $0.name.value })
24+
25+
let missingArguments = requiredArguments.subtracting(providedArguments)
26+
if !missingArguments.isEmpty {
27+
context.report(error: GraphQLError(
28+
message: missingArgumentsMessage(
29+
fieldName: field.name,
30+
type: type.name,
31+
missingArguments: Array(missingArguments)
32+
),
33+
nodes: [node]
34+
))
35+
}
36+
}
37+
38+
return .continue
39+
}
40+
)
41+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ let specifiedRules: [(ValidationContext) -> Visitor] = [
1818
// NoUndefinedVariables,
1919
// NoUnusedVariables,
2020
// KnownDirectives,
21-
// KnownArgumentNames,
21+
KnownArgumentNames,
2222
// UniqueArgumentNames,
2323
// ArgumentsOfCorrectType,
24-
// ProvidedNonNullArguments,
24+
ProvidedNonNullArguments,
2525
// DefaultValuesOfCorrectType,
2626
// VariablesInAllowedPosition,
2727
// OverlappingFieldsCanBeMerged,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class KnownArgumentNamesTests : ValidationTestCase {
5+
override func setUp() {
6+
rule = KnownArgumentNames
7+
}
8+
9+
func testValidWithObjectWithoutArguments() throws {
10+
try assertValid(
11+
"fragment objectFieldSelection on Dog { __typename name }"
12+
)
13+
}
14+
15+
func testValidWithCorrectArgumentNames() throws {
16+
try assertValid(
17+
"fragment objectFieldSelection on Dog { __typename isHousetrained(atOtherHomes: true) }"
18+
)
19+
}
20+
21+
func testInvalidWithSlightlyMisspelledArgument() throws {
22+
let errors = try assertInvalid(
23+
errorCount: 1,
24+
query: "fragment objectFieldSelection on Dog { __typename isHousetrained(atOtherHomees: true) }"
25+
)
26+
27+
try assertValidationError(
28+
error: errors.first, line: 1, column: 66,
29+
message: #"Field "isHousetrained" on type "Dog" does not have argument "atOtherHomees". Did you mean "atOtherHomes"?"#
30+
)
31+
}
32+
33+
func testInvalidWithUnrelatedArgument() throws {
34+
let errors = try assertInvalid(
35+
errorCount: 1,
36+
query: "fragment objectFieldSelection on Dog { __typename name(uppercased: true) }"
37+
)
38+
39+
try assertValidationError(
40+
error: errors.first, line: 1, column: 56,
41+
message: #"Field "name" on type "Dog" does not have argument "uppercased"."#
42+
)
43+
}
44+
45+
}
46+
47+
extension KnownArgumentNamesTests {
48+
static var allTests: [(String, (KnownArgumentNamesTests) -> () throws -> Void)] {
49+
return [
50+
("testValidWithObjectWithoutArguments", testValidWithObjectWithoutArguments),
51+
("testValidWithCorrectArgumentNames", testValidWithCorrectArgumentNames),
52+
("testInvalidWithSlightlyMisspelledArgument", testInvalidWithSlightlyMisspelledArgument),
53+
("testInvalidWithUnrelatedArgument", testInvalidWithUnrelatedArgument),
54+
]
55+
}
56+
}

0 commit comments

Comments
 (0)