Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Conditional logic using GraphQLNullable variables #2794

Merged
merged 3 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Sources/ApolloAPI/Selection+Conditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,19 @@ fileprivate extension Array where Element == Selection.Condition {
// MARK: Conditions - Individual
fileprivate extension Selection.Condition {
func evaluate(with variables: GraphQLOperation.Variables?) -> Bool {
guard let boolValue = variables?[variableName] as? Bool else {
switch variables?[variableName] {
case let boolValue as Bool:
return inverted ? !boolValue : boolValue

case let nullable as GraphQLNullable<Bool>:
let evaluated = nullable.unwrapped ?? false
return inverted ? !evaluated : evaluated

case .none:
return false

case let .some(wrapped):
fatalError("Expected Bool for \(variableName), got \(wrapped)")
}
return inverted ? !boolValue : boolValue
}

}
107 changes: 0 additions & 107 deletions Tests/ApolloServerIntegrationTests/StarWarsServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,113 +214,6 @@ class StarWarsServerTests: XCTestCase, CacheDependentTesting {
}
}

// MARK: @skip / @include directives

#warning("""
TODO: This functionality is all tested by skip/include tests on
GraphQLExecutor_SelectionSetMapper_FromResponse_Tests.
We just need to test that the selection sets for these are generated correctly by codegen
""")
// func testHeroNameConditionalExclusion() {
// fetch(query: HeroNameConditionalExclusionQuery(skipName: false)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalExclusionQuery(skipName: true)) { data in
// XCTAssertNil(data.hero?.name)
// }
// }
//
// func testHeroNameConditionalInclusion() {
// fetch(query: HeroNameConditionalInclusionQuery(includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalInclusionQuery(includeName: false)) { data in
// XCTAssertNil(data.hero?.name)
// }
// }
//
// func testHeroNameConditionalBoth() {
// fetch(query: HeroNameConditionalBothQuery(skipName: false, includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalBothQuery(skipName: true, includeName: true)) { data in
// XCTAssertNil(data.hero?.name)
// }
//
// fetch(query: HeroNameConditionalBothQuery(skipName: false, includeName: false)) { data in
// XCTAssertNil(data.hero?.name)
// }
//
// fetch(query: HeroNameConditionalBothQuery(skipName: true, includeName: false)) { data in
// XCTAssertNil(data.hero?.name)
// }
// }
//
// func testHeroNameConditionalBothSeparate() {
// fetch(query: HeroNameConditionalBothSeparateQuery(skipName: false, includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalBothSeparateQuery(skipName: true, includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalBothSeparateQuery(skipName: false, includeName: false)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// }
//
// fetch(query: HeroNameConditionalBothSeparateQuery(skipName: true, includeName: false)) { data in
// XCTAssertNil(data.hero?.name)
// }
// }
//
// func testHeroDetailsInlineConditionalInclusion() {
// fetch(query: HeroDetailsInlineConditionalInclusionQuery(includeDetails: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// XCTAssertEqual(data.hero?.appearsIn, [.newhope, .empire, .jedi])
// }
//
// fetch(query: HeroDetailsInlineConditionalInclusionQuery(includeDetails: false)) { data in
// XCTAssertNil(data.hero?.name)
// XCTAssertNil(data.hero?.appearsIn)
// }
// }
//
// func testHeroDetailsFragmentConditionalInclusion() {
// fetch(query: HeroDetailsFragmentConditionalInclusionQuery(includeDetails: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// XCTAssertEqual(data.hero?.asDroid?.primaryFunction, "Astromech")
// }
//
// fetch(query: HeroDetailsFragmentConditionalInclusionQuery(includeDetails: false)) { data in
// XCTAssertNil(data.hero?.name)
// XCTAssertNil(data.hero?.asDroid?.primaryFunction)
// }
// }
//
// func testHeroNameTypeSpecificConditionalInclusion() {
// fetch(query: HeroNameTypeSpecificConditionalInclusionQuery(includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// XCTAssertEqual(data.hero?.asDroid?.name, "R2-D2")
// }
//
// fetch(query: HeroNameTypeSpecificConditionalInclusionQuery(includeName: false)) { data in
// XCTAssertEqual(data.hero?.name, "R2-D2")
// XCTAssertEqual(data.hero?.asDroid?.name, "R2-D2")
// }
//
// fetch(query: HeroNameTypeSpecificConditionalInclusionQuery(episode: .empire, includeName: true)) { data in
// XCTAssertEqual(data.hero?.name, "Luke Skywalker")
// }
//
// fetch(query: HeroNameTypeSpecificConditionalInclusionQuery(episode: .empire, includeName: false)) { data in
// XCTAssertNil(data.hero?.name)
// }
// }

// MARK: Mutations

func testCreateReviewForEpisode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase {
// MARK: - Boolean Conditions

// MARK: Include

func test__booleanCondition_include_singleField__givenVariableIsTrue_getsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
Expand Down Expand Up @@ -964,6 +965,40 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase {
expect(data.name).to(beNil())
}

func test__booleanCondition_include_singleField__givenGraphQLNullableVariableIsTrue_getsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
override class var __selections: [Selection] {[
.include(if: "variable", .field("name", String.self))
]}
}
let object: JSONObject = ["name": "Luke Skywalker"]
let variables = ["variable": GraphQLNullable<Bool>(true)]

// when
let data = try readValues(GivenSelectionSet.self, from: object, variables: variables)

// then
expect(data.name).to(equal("Luke Skywalker"))
}

func test__booleanCondition_include_singleField__givenGraphQLNullableVariableIsFalse_doesNotGetsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
override class var __selections: [Selection] {[
.include(if: "variable", .field("name", String.self))
]}
}
let object: JSONObject = ["name": "Luke Skywalker"]
let variables = ["variable": GraphQLNullable<Bool>(false)]

// when
let data = try readValues(GivenSelectionSet.self, from: object, variables: variables)

// then
expect(data.name).to(beNil())
}

func test__booleanCondition_multipleIncludes_singleField__givenAllVariablesAreTrue_getsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
Expand Down Expand Up @@ -1302,6 +1337,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase {
}

// MARK: Skip

func test__booleanCondition_skip_singleField__givenVariableIsFalse_getsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
Expand Down Expand Up @@ -1336,6 +1372,40 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase {
expect(data.name).to(beNil())
}

func test__booleanCondition_skip_singleField__givenGraphQLNullableVariableIsFalse_getsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
override class var __selections: [Selection] {[
.include(if: !"variable", .field("name", String.self))
]}
}
let object: JSONObject = ["name": "Luke Skywalker"]
let variables = ["variable": GraphQLNullable<Bool>(false)]

// when
let data = try readValues(GivenSelectionSet.self, from: object, variables: variables)

// then
expect(data.name).to(equal("Luke Skywalker"))
}

func test__booleanCondition_skip_singleField__givenGraphQLNullableVariableIsTrue_doesNotGetsValueForConditionalField() throws {
// given
class GivenSelectionSet: MockSelectionSet {
override class var __selections: [Selection] {[
.include(if: !"variable", .field("name", String.self))
]}
}
let object: JSONObject = ["name": "Luke Skywalker"]
let variables = ["variable": GraphQLNullable<Bool>(true)]

// when
let data = try readValues(GivenSelectionSet.self, from: object, variables: variables)

// then
expect(data.name).to(beNil())
}

func test__booleanCondition_skip_multipleFields__givenVariableIsFalse_getsValuesForConditionalFields() throws {
// given
class GivenSelectionSet: MockSelectionSet {
Expand Down