From ae7983df7cd4a68595c73f34f61ec4b26ee31eaa Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Tue, 24 Sep 2024 17:44:28 -0600 Subject: [PATCH] fix: Fixes default value introspection Input object and boolean default values are represented correctly in introspection --- Sources/GraphQL/Type/Introspection.swift | 11 +- Sources/GraphQL/Utilities/ASTFromValue.swift | 5 + .../TypeTests/IntrospectionTests.swift | 274 ++++++++++++++++++ 3 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 Tests/GraphQLTests/TypeTests/IntrospectionTests.swift diff --git a/Sources/GraphQL/Type/Introspection.swift b/Sources/GraphQL/Type/Introspection.swift index 013433d6..c8970109 100644 --- a/Sources/GraphQL/Type/Introspection.swift +++ b/Sources/GraphQL/Type/Introspection.swift @@ -370,7 +370,16 @@ let __InputValue = try! GraphQLObjectType( return nil } - return .string(defaultValue.description) + if defaultValue == .null || defaultValue == .undefined { + return defaultValue + } + + guard let literal = try astFromValue(value: defaultValue, type: inputValue.type) + else { + throw GraphQLError(message: "Invalid default value") + } + + return .string(print(ast: literal)) } ), ] diff --git a/Sources/GraphQL/Utilities/ASTFromValue.swift b/Sources/GraphQL/Utilities/ASTFromValue.swift index 92671622..f5c721a7 100644 --- a/Sources/GraphQL/Utilities/ASTFromValue.swift +++ b/Sources/GraphQL/Utilities/ASTFromValue.swift @@ -97,6 +97,11 @@ func astFromValue( } // Others serialize based on their corresponding scalar types. + if case let .bool(bool) = serialized { + return BooleanValue(value: bool) + } + + // JavaScript numbers can be Int or Float values. if case let .number(number) = serialized { switch number.storageType { case .bool: diff --git a/Tests/GraphQLTests/TypeTests/IntrospectionTests.swift b/Tests/GraphQLTests/TypeTests/IntrospectionTests.swift new file mode 100644 index 00000000..0a990391 --- /dev/null +++ b/Tests/GraphQLTests/TypeTests/IntrospectionTests.swift @@ -0,0 +1,274 @@ +@testable import GraphQL +import NIO +import XCTest + +class IntrospectionTests: XCTestCase { + private var eventLoopGroup: EventLoopGroup! + + override func setUp() { + eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + } + + override func tearDown() { + XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) + } + + func testDefaultValues() throws { + let numEnum = try GraphQLEnumType( + name: "Enum", + values: [ + "One": .init(value: "One"), + "Two": .init(value: "Two"), + ] + ) + let inputObject = try GraphQLInputObjectType( + name: "InputObject", + fields: ["str": .init(type: GraphQLString)] + ) + let outputObject = try GraphQLObjectType( + name: "Object", + fields: ["str": .init(type: GraphQLString)] + ) + + let query = try GraphQLObjectType( + name: "Query", + fields: [ + "bool": .init( + type: GraphQLBoolean, + args: [ + "bool": .init( + type: GraphQLBoolean, + defaultValue: true + ), + ] + ), + "enum": .init( + type: numEnum, + args: [ + "enum": .init( + type: numEnum, + defaultValue: "One" + ), + ] + ), + "float": .init( + type: GraphQLFloat, + args: [ + "float": .init( + type: GraphQLFloat, + defaultValue: 2.2 + ), + ] + ), + "id": .init( + type: GraphQLID, + args: [ + "id": .init( + type: GraphQLID, + defaultValue: "5" + ), + ] + ), + "int": .init( + type: GraphQLInt, + args: [ + "int": .init( + type: GraphQLInt, + defaultValue: 5 + ), + ] + ), + "list": .init( + type: GraphQLList(GraphQLInt), + args: [ + "list": .init( + type: GraphQLList(GraphQLInt), + defaultValue: [1, 2, 3] + ), + ] + ), + "object": .init( + type: outputObject, + args: [ + "input": .init( + type: inputObject, + defaultValue: ["str": "hello"] + ), + ] + ), + "string": .init( + type: GraphQLString, + args: [ + "string": .init( + type: GraphQLString, + defaultValue: "hello" + ), + ] + ), + ] + ) + + let schema = try GraphQLSchema(query: query, types: [inputObject, outputObject]) + + let introspection = try graphql( + schema: schema, + request: """ + query IntrospectionTypeQuery { + __schema { + types { + fields { + args { + defaultValue + name + type { + name + } + } + name + type { + name + } + } + name + } + } + } + """, + eventLoopGroup: eventLoopGroup + ).wait() + + let queryType = try XCTUnwrap( + introspection.data?["__schema"]["types"].array? + .find { $0["name"] == "Query" } + ) + + XCTAssertEqual( + queryType, + [ + "fields": [ + [ + "args": [ + [ + "defaultValue": "true", + "name": "bool", + "type": [ + "name": "Boolean", + ], + ], + ], + "name": "bool", + "type": [ + "name": "Boolean", + ], + ], + [ + "args": [ + [ + "defaultValue": "One", + "name": "enum", + "type": [ + "name": "Enum", + ], + ], + ], + "name": "enum", + "type": [ + "name": "Enum", + ], + ], + [ + "args": [ + [ + "defaultValue": "2.2", + "name": "float", + "type": [ + "name": "Float", + ], + ], + ], + "name": "float", + "type": [ + "name": "Float", + ], + ], + [ + "args": [ + [ + "defaultValue": "5", + "name": "id", + "type": [ + "name": "ID", + ], + ], + ], + "name": "id", + "type": [ + "name": "ID", + ], + ], + [ + "args": [ + [ + "defaultValue": "5", + "name": "int", + "type": [ + "name": "Int", + ], + ], + ], + "name": "int", + "type": [ + "name": "Int", + ], + ], + [ + "args": [ + [ + "defaultValue": "[1, 2, 3]", + "name": "list", + "type": [ + "name": .null, + ], + ], + ], + "name": "list", + "type": [ + "name": .null, + ], + ], + [ + "args": [ + [ + "defaultValue": "{ str: \"hello\" }", + "name": "input", + "type": [ + "name": "InputObject", + ], + ], + ], + "name": "object", + "type": [ + "name": "Object", + ], + ], + [ + "args": [ + [ + "defaultValue": "\"hello\"", + "name": "string", + "type": [ + "name": "String", + ], + ], + ], + "name": "string", + "type": [ + "name": "String", + ], + ], + ], + "name": "Query", + ] + ) + } +}