diff --git a/codegen/lib/graphql_swift_gen/templates/type.swift.erb b/codegen/lib/graphql_swift_gen/templates/type.swift.erb index e565b1f..693512f 100644 --- a/codegen/lib/graphql_swift_gen/templates/type.swift.erb +++ b/codegen/lib/graphql_swift_gen/templates/type.swift.erb @@ -213,16 +213,22 @@ extension <%= schema_name %> { } <% when 'INPUT_OBJECT' %> open class <%= type.name %> { - <% type.input_fields.each do |field| %> - open var <%= escape_reserved_word(field.camelize_name) %>: <%= swift_input_type(field.type) %> + <% type.required_input_fields.each do |field| %> + open var <%= escape_reserved_word(field.camelize_name) %>: <%= swift_input_type(field.type, non_null: true) %> + <% end %> + <% type.optional_input_fields.each do |field| %> + open var <%= escape_reserved_word(field.camelize_name) %>: Input<<%= swift_input_type(field.type, non_null: true) %>> <% end %> public init( <% input_fields = type.required_input_fields + type.optional_input_fields %> <% input_fields.each do |field| %> - <% default = field.type.non_null? ? "" : " = nil" %> <% seperator = field == input_fields.last ? "" : "," %> - <%= escape_reserved_word(field.camelize_name) %>: <%= swift_input_type(field.type) %><%= default %><%= seperator %> + <% if field.type.non_null? %> + <%= escape_reserved_word(field.camelize_name) %>: <%= swift_input_type(field.type, non_null: true) %><%= seperator %> + <% else %> + <%= escape_reserved_word(field.camelize_name) %>: Input<<%= swift_input_type(field.type, non_null: true) %>> = .undefined<%= seperator %> + <% end %> <% end %> ) { <% type.input_fields.each do |field| %> @@ -232,14 +238,19 @@ extension <%= schema_name %> { func serialize() -> String { var fields: [String] = [] - <% type.input_fields.each do |field| %> - <% unless field.type.non_null? %> + <% type.required_input_fields.each do |field| %> + fields.append("<%= field.name %>:<%= generate_build_input_code(field.camelize_name, field.type.unwrap_non_null) %>") + <% end %> + <% type.optional_input_fields.each do |field| %> + switch <%= escape_reserved_word(field.camelize_name) %> { + case .value(let <%= escape_reserved_word(field.camelize_name) %>): if let <%= escape_reserved_word(field.camelize_name) %> = <%= escape_reserved_word(field.camelize_name) %> { - <% end %> fields.append("<%= field.name %>:<%= generate_build_input_code(field.camelize_name, field.type.unwrap_non_null) %>") - <% unless field.type.non_null? %> + } else { + fields.append("<%= field.name %>:null") } - <% end %> + case .undefined: break + } <% end %> return "{\(fields.joined(separator: ","))}" } diff --git a/support/Sources/GraphQL.swift b/support/Sources/GraphQL.swift index 22cbe27..c90cc32 100644 --- a/support/Sources/GraphQL.swift +++ b/support/Sources/GraphQL.swift @@ -294,6 +294,21 @@ public struct SchemaViolationError: Error { } } +public enum Input { + // Serialzeable value + case value(T?) + // Not serializable + case undefined + + public init(orUndefined optional: Optional) { + if let value = optional { + self = .value(value) + } else { + self = .undefined + } + } +} + extension GraphQL.Selection: Equatable {} public func ==(lhs: GraphQL.Selection, rhs: GraphQL.Selection) -> Bool { return (lhs === rhs) || (lhs.field == rhs.field && lhs.alias == rhs.alias && lhs.args == rhs.args && lhs.subfields == rhs.subfields) diff --git a/support/Tests/GraphQLSupportTests/Generated/SetIntegerInput.swift b/support/Tests/GraphQLSupportTests/Generated/SetIntegerInput.swift index 9109f7d..79cf77e 100644 --- a/support/Tests/GraphQLSupportTests/Generated/SetIntegerInput.swift +++ b/support/Tests/GraphQLSupportTests/Generated/SetIntegerInput.swift @@ -9,42 +9,26 @@ extension Generated { open var value: Int32 - open var ttl: Date? { - didSet { - ttlSeen = true - } - } - private var ttlSeen: Bool = false + open var ttl: Input - open var negate: Bool? { - didSet { - negateSeen = true - } - } - private var negateSeen: Bool = false + open var negate: Input public init( key: String, value: Int32, - ttl: Date?? = nil, + ttl: Input = .undefined, - negate: Bool?? = nil + negate: Input = .undefined ) { self.key = key self.value = value - if let ttl = ttl { - self.ttlSeen = true - self.ttl = ttl - } + self.ttl = ttl - if let negate = negate { - self.negateSeen = true - self.negate = negate - } + self.negate = negate } func serialize() -> String { @@ -54,20 +38,24 @@ extension Generated { fields.append("value:\(value)") - if ttlSeen { + switch ttl { + case .value(let ttl): if let ttl = ttl { fields.append("ttl:\(GraphQL.quoteString(input: "\(iso8601DateParser.string(from: ttl))"))") } else { fields.append("ttl:null") } + case .undefined: break } - if negateSeen { + switch negate { + case .value(let negate): if let negate = negate { fields.append("negate:\(negate)") } else { fields.append("negate:null") } + case .undefined: break } return "{\(fields.joined(separator: ","))}" diff --git a/support/Tests/GraphQLSupportTests/IntegrationTests.swift b/support/Tests/GraphQLSupportTests/IntegrationTests.swift index fc028f6..f5cbf9c 100644 --- a/support/Tests/GraphQLSupportTests/IntegrationTests.swift +++ b/support/Tests/GraphQLSupportTests/IntegrationTests.swift @@ -62,16 +62,50 @@ class IntegrationTests: XCTestCase { func testInputObject() { let query = Generated.buildMutation { $0 - .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, negate: true)) + .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, negate: Input(orUndefined: true))) } let queryString = String(describing: query) XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42,negate:true})}") } + func testInputObjectExplicitUndefined() { + let query = Generated.buildMutation { $0 + .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, negate: .undefined)) + } + let queryString = String(describing: query) + XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42})}") + } + + func testInputObjectOrUndefined() { + let query = Generated.buildMutation { $0 + .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, negate: Input(orUndefined: nil))) + } + let queryString = String(describing: query) + XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42})}") + } + + func testInputObjectExplictNullConstructor() { + let query = Generated.buildMutation { $0 + .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, negate: .value(nil))) + } + let queryString = String(describing: query) + XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42,negate:null})}") + } + + func testInputObjectExplictNullSetLater() { + let input = Generated.SetIntegerInput(key: "answer", value: 42) + input.negate = .value(nil) + let query = Generated.buildMutation { $0 + .setInteger(input: input) + } + let queryString = String(describing: query) + XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42,negate:null})}") + } + func testScalarInput() { let ttl = date(year: 2017, month: 1, day: 31, hour: 10, minute: 9, second: 48) let query = Generated.buildMutation { $0 - .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, ttl: ttl)) + .setInteger(input: Generated.SetIntegerInput(key: "answer", value: 42, ttl: .value(ttl))) } let queryString = String(describing: query) XCTAssertEqual(queryString, "mutation{set_integer(input:{key:\"answer\",value:42,ttl:\"2017-01-31T10:09:48Z\"})}")