From 6d057c70465557ce52838516436282db348fbcfd Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Tue, 9 Nov 2021 13:16:03 -0800 Subject: [PATCH] AST Field Model (#2023) * Fixed ordering of sorted selections for merged fields * WIP: Creating ASTField * Invert ASTField and ASTFieldType * rename object to entity, as it can be an object, interface, or union * More renames * Fix equality * Refactor to expose GraphQLType --- Apollo.xcodeproj/project.pbxproj | 12 ++- Sources/ApolloCodegenLib/AST/ASTField.swift | 74 +++++++++++++++ .../AST/ASTSelectionSet.swift | 2 +- .../AST/MergedSelectionBuilder.swift | 11 ++- .../AST/ScopedSelectionSetHashable.swift | 6 ++ .../SelectionMergable.swift} | 17 ++++ .../AST/SortedSelections.swift | 30 +++--- SwiftScripts/Package.resolved | 9 ++ SwiftScripts/Sources/Codegen/Target.swift | 2 +- .../AnimalKingdomASTCreationTests.swift | 92 +++++++++---------- .../Queries/AllAnimalsQuery.swift | 40 ++++---- 11 files changed, 205 insertions(+), 90 deletions(-) create mode 100644 Sources/ApolloCodegenLib/AST/ASTField.swift rename Sources/ApolloCodegenLib/{Frontend/CompilationResult+SelectionSetMerging.swift => AST/SelectionMergable.swift} (86%) diff --git a/Apollo.xcodeproj/project.pbxproj b/Apollo.xcodeproj/project.pbxproj index 609566a39b..e0b9ce6c26 100644 --- a/Apollo.xcodeproj/project.pbxproj +++ b/Apollo.xcodeproj/project.pbxproj @@ -201,7 +201,7 @@ DE223C292720B897004A0148 /* StringInterpolation+NestedIndentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE223C282720B897004A0148 /* StringInterpolation+NestedIndentation.swift */; }; DE223C2D2721FCE8004A0148 /* ScopedSelectionSetHashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE223C2C2721FCE8004A0148 /* ScopedSelectionSetHashable.swift */; }; DE223C3327221144004A0148 /* ASTMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE223C2A2721FAD6004A0148 /* ASTMatchers.swift */; }; - DE223C3527223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE223C3427223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift */; }; + DE223C3527223E2A004A0148 /* SelectionMergable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE223C3427223E2A004A0148 /* SelectionMergable.swift */; }; DE2FCF1D26E806710057EA67 /* SchemaConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF1C26E806710057EA67 /* SchemaConfiguration.swift */; }; DE2FCF1F26E807CC0057EA67 /* CacheTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */; }; DE2FCF2126E807EF0057EA67 /* Cacheable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2FCF2026E807EF0057EA67 /* Cacheable.swift */; }; @@ -287,6 +287,7 @@ DED46035261CEA660086EF63 /* ApolloTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F8A95781EC0FC1200304A2D /* ApolloTestSupport.framework */; }; DED46042261CEA8A0086EF63 /* TestServerURLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED45C172615308E0086EF63 /* TestServerURLs.swift */; }; DED46051261CEAD20086EF63 /* StarWarsAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FCE2CFA1E6C213D00E34457 /* StarWarsAPI.framework */; }; + DEFBBC86273470F70088AABC /* ASTField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBBC85273470F70088AABC /* ASTField.swift */; }; E616B6D126C3335600DB049E /* ExecutionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E616B6D026C3335600DB049E /* ExecutionTests.swift */; }; E61DD76526D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */; }; E657CDBA26FD01D4005834D6 /* ApolloSchemaInternalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E657CDB926FD01D4005834D6 /* ApolloSchemaInternalTests.swift */; }; @@ -833,7 +834,7 @@ DE223C282720B897004A0148 /* StringInterpolation+NestedIndentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StringInterpolation+NestedIndentation.swift"; sourceTree = ""; }; DE223C2A2721FAD6004A0148 /* ASTMatchers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTMatchers.swift; sourceTree = ""; }; DE223C2C2721FCE8004A0148 /* ScopedSelectionSetHashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScopedSelectionSetHashable.swift; sourceTree = ""; }; - DE223C3427223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompilationResult+SelectionSetMerging.swift"; sourceTree = ""; }; + DE223C3427223E2A004A0148 /* SelectionMergable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionMergable.swift; sourceTree = ""; }; DE2FCF1C26E806710057EA67 /* SchemaConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaConfiguration.swift; sourceTree = ""; }; DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTransaction.swift; sourceTree = ""; }; DE2FCF2026E807EF0057EA67 /* Cacheable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cacheable.swift; sourceTree = ""; }; @@ -934,6 +935,7 @@ DEE1B3F3273B08D8007350E5 /* AllAnimals.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllAnimals.graphql.swift; sourceTree = ""; }; DEE1B3F4273B08D8007350E5 /* Types.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.graphql.swift; sourceTree = ""; }; DEE1B3F5273B08D8007350E5 /* WarmBloodedDetails.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarmBloodedDetails.graphql.swift; sourceTree = ""; }; + DEFBBC85273470F70088AABC /* ASTField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTField.swift; sourceTree = ""; }; E616B6D026C3335600DB049E /* ExecutionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExecutionTests.swift; sourceTree = ""; }; E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLiteDotSwiftDatabaseBehaviorTests.swift; sourceTree = ""; }; E657CDB926FD01D4005834D6 /* ApolloSchemaInternalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApolloSchemaInternalTests.swift; sourceTree = ""; }; @@ -1731,7 +1733,6 @@ 9F628EB42593651B00F94F9D /* GraphQLValue.swift */, 9F62E03E2590896400E6E808 /* GraphQLError.swift */, 9F1A9668258F34BB00A06EEB /* CompilationResult.swift */, - DE223C3427223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift */, 9F1A966A258F34BB00A06EEB /* JavaScriptBridge.swift */, DE3C7973260A646300D2F4FF /* dist */, ); @@ -1972,9 +1973,11 @@ isa = PBXGroup; children = ( DEAFB7782705274B00BE02F3 /* ASTSelectionSet.swift */, + DEFBBC85273470F70088AABC /* ASTField.swift */, DE223C2C2721FCE8004A0148 /* ScopedSelectionSetHashable.swift */, DE09114D27288B1F000648E5 /* SortedSelections.swift */, DE7C183D272A154400727031 /* MergedSelectionBuilder.swift */, + DE223C3427223E2A004A0148 /* SelectionMergable.swift */, DE7C183B272A12EB00727031 /* TypeScopeDescriptor.swift */, ); path = AST; @@ -2799,6 +2802,7 @@ 9F1A966F258F34BB00A06EEB /* JavaScriptBridge.swift in Sources */, 9BAEEBF72346F0A000808306 /* StaticString+Apollo.swift in Sources */, 9BCA8C0926618226004FF2F6 /* UntypedGraphQLRequestBodyCreator.swift in Sources */, + DEFBBC86273470F70088AABC /* ASTField.swift in Sources */, 9F62DFD02590710E00E6E808 /* GraphQLSource.swift in Sources */, 9BAEEBF32346DDAD00808306 /* CodegenLogger.swift in Sources */, 9F628EB52593651B00F94F9D /* GraphQLValue.swift in Sources */, @@ -2809,7 +2813,7 @@ 9F628E9525935BE600F94F9D /* GraphQLType.swift in Sources */, 9BFE8DA9265D5D8F000BBF81 /* URLDownloader.swift in Sources */, 9F1A966D258F34BB00A06EEB /* CompilationResult.swift in Sources */, - DE223C3527223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift in Sources */, + DE223C3527223E2A004A0148 /* SelectionMergable.swift in Sources */, 9B7B6F69233C2C0C00F32205 /* FileManager+Apollo.swift in Sources */, 9F1A966C258F34BB00A06EEB /* GraphQLSchema.swift in Sources */, 9BE74D3D23FB4A8E006D354F /* FileFinder.swift in Sources */, diff --git a/Sources/ApolloCodegenLib/AST/ASTField.swift b/Sources/ApolloCodegenLib/AST/ASTField.swift new file mode 100644 index 0000000000..cf015fc04e --- /dev/null +++ b/Sources/ApolloCodegenLib/AST/ASTField.swift @@ -0,0 +1,74 @@ +import Foundation + +@dynamicMemberLookup +struct ASTField: Equatable { + enum FieldType: Equatable { + case scalar(GraphQLType) + case entity(EntityFieldData) + } + + struct EntityFieldData: Equatable { + let type: GraphQLType + let selectionSet: CompilationResult.SelectionSet + let enclosingEntityMergedSelectionBuilder: MergedSelectionBuilder + } + + let underlyingField: CompilationResult.Field + let type: FieldType + + init(_ field: CompilationResult.Field, + enclosingScopeMergedSelectionBuilder: MergedSelectionBuilder? = nil) { + self.underlyingField = field + self.type = FieldType( + self.underlyingField, + enclosingScopeMergedSelectionBuilder: enclosingScopeMergedSelectionBuilder + ) + } + + subscript(dynamicMember keyPath: KeyPath) -> V { + get { + underlyingField[keyPath: keyPath] + } + } + + static func ==(lhs: ASTField, rhs: ASTField) -> Bool { + lhs.underlyingField == rhs.underlyingField && + lhs.type == rhs.type + } +} + +extension ASTField.FieldType { + init(_ field: CompilationResult.Field, + enclosingScopeMergedSelectionBuilder: MergedSelectionBuilder?) { + switch field.type.namedType { + case is GraphQLScalarType, is GraphQLEnumType: + self = .scalar(field.type) + + case is GraphQLCompositeType: + guard let selectionSet = field.selectionSet else { + fatalError("Invalid field: \(field). An object type field must contain a selection set.") + } + guard let enclosingScopeMergedSelectionBuilder = enclosingScopeMergedSelectionBuilder else { + fatalError("enclosingScopeMergedSelectionBuilder must be provided for object type field.") + } + + self = .entity( + ASTField.EntityFieldData( + type: field.type, + selectionSet: selectionSet, + enclosingEntityMergedSelectionBuilder: enclosingScopeMergedSelectionBuilder + ) + ) + + default: + fatalError("Field \(field) must have a base type of scalar, enum, interface, union, or object. Got \(field.type.namedType)") + } + } +} + +extension ASTField.EntityFieldData { + static func == (lhs: ASTField.EntityFieldData, rhs: ASTField.EntityFieldData) -> Bool { + lhs.selectionSet == rhs.selectionSet && + lhs.enclosingEntityMergedSelectionBuilder === rhs.enclosingEntityMergedSelectionBuilder + } +} diff --git a/Sources/ApolloCodegenLib/AST/ASTSelectionSet.swift b/Sources/ApolloCodegenLib/AST/ASTSelectionSet.swift index 979030836f..45cb47dbdc 100644 --- a/Sources/ApolloCodegenLib/AST/ASTSelectionSet.swift +++ b/Sources/ApolloCodegenLib/AST/ASTSelectionSet.swift @@ -23,7 +23,7 @@ class ASTSelectionSet: CustomDebugStringConvertible, Equatable { let mergedSelectionBuilder: MergedSelectionBuilder - // MARK: - Initialization + // MARK: - Initialization convenience init( selectionSet: CompilationResult.SelectionSet, diff --git a/Sources/ApolloCodegenLib/AST/MergedSelectionBuilder.swift b/Sources/ApolloCodegenLib/AST/MergedSelectionBuilder.swift index badfe281f6..89a7b4634c 100644 --- a/Sources/ApolloCodegenLib/AST/MergedSelectionBuilder.swift +++ b/Sources/ApolloCodegenLib/AST/MergedSelectionBuilder.swift @@ -11,15 +11,16 @@ import OrderedCollections /// representing a different entity. Each new root parent `ASTSelectionSet` should create a new /// `MergedSelectionBuilder` for its child tree. class MergedSelectionBuilder { - var selectionsForScopes: OrderedDictionary = [:] + private(set) var selectionsForScopes: OrderedDictionary = [:] + private(set) var fieldSelectionMergedScopes: [String: MergedSelectionBuilder] = [:] func add(_ selections: SortedSelections, forScope typeScope: TypeScope) { - if var selectionsForScope = selectionsForScopes[typeScope] { - selectionsForScope.mergeIn(selections) - selectionsForScopes[typeScope] = selectionsForScope + if var existingSelections = selectionsForScopes[typeScope] { + existingSelections.mergeIn(selections) + selectionsForScopes[typeScope] = existingSelections } else { - selectionsForScopes[typeScope] = selections + selectionsForScopes.updateValue(selections, forKey: typeScope, insertingAt: 0) } } diff --git a/Sources/ApolloCodegenLib/AST/ScopedSelectionSetHashable.swift b/Sources/ApolloCodegenLib/AST/ScopedSelectionSetHashable.swift index 270a76f7a0..0b5e6af5d2 100644 --- a/Sources/ApolloCodegenLib/AST/ScopedSelectionSetHashable.swift +++ b/Sources/ApolloCodegenLib/AST/ScopedSelectionSetHashable.swift @@ -30,6 +30,12 @@ extension CompilationResult.Field: ScopedSelectionSetHashable { } } +extension ASTField: ScopedSelectionSetHashable { + var hashForSelectionSetScope: String { + underlyingField.hashForSelectionSetScope + } +} + extension CompilationResult.SelectionSet: ScopedSelectionSetHashable { var hashForSelectionSetScope: String { #warning("What if there is a field with the same name as a type? Do we get a conflict? Write a test for this.") diff --git a/Sources/ApolloCodegenLib/Frontend/CompilationResult+SelectionSetMerging.swift b/Sources/ApolloCodegenLib/AST/SelectionMergable.swift similarity index 86% rename from Sources/ApolloCodegenLib/Frontend/CompilationResult+SelectionSetMerging.swift rename to Sources/ApolloCodegenLib/AST/SelectionMergable.swift index a84b057692..b96445d55d 100644 --- a/Sources/ApolloCodegenLib/Frontend/CompilationResult+SelectionSetMerging.swift +++ b/Sources/ApolloCodegenLib/AST/SelectionMergable.swift @@ -68,6 +68,23 @@ extension CompilationResult.Field: SelectionMergable { } +extension ASTField: SelectionMergable { + var _selectionSet: CompilationResult.SelectionSet? { + underlyingField.selectionSet + } + + func merging(_ newSelectionSet: CompilationResult.SelectionSet) -> ASTField { + switch self.type { + case .scalar, .enum: + fatalError("Selection sets should never be merged into a scalar or enum type field.") + + case .entity: + return ASTField(self.underlyingField.merging(newSelectionSet)) + } + } + +} + extension CompilationResult.Selection: SelectionMergable { var _selectionSet: CompilationResult.SelectionSet? { diff --git a/Sources/ApolloCodegenLib/AST/SortedSelections.swift b/Sources/ApolloCodegenLib/AST/SortedSelections.swift index b5c8ec852b..640770fd94 100644 --- a/Sources/ApolloCodegenLib/AST/SortedSelections.swift +++ b/Sources/ApolloCodegenLib/AST/SortedSelections.swift @@ -3,7 +3,7 @@ import OrderedCollections struct SortedSelections: Equatable, CustomDebugStringConvertible { typealias Selection = CompilationResult.Selection - typealias Field = CompilationResult.Field + typealias Field = ASTField typealias TypeCase = CompilationResult.SelectionSet typealias Fragment = CompilationResult.FragmentDefinition @@ -47,19 +47,19 @@ struct SortedSelections: Equatable, CustomDebugStringConvertible { // MARK: Selection Merging - @inlinable mutating func mergeIn(_ selections: SortedSelections) { + mutating func mergeIn(_ selections: SortedSelections) { mergeIn(selections.fields) mergeIn(typeCases: selections.typeCases) mergeIn(selections.fragments) } - @inlinable mutating func mergeIn(_ selections: T) where T.Element == Selection { + mutating func mergeIn(_ selections: T) where T.Element == Selection { for selection in selections { mergeIn(selection) } } - @inlinable mutating func mergeIn(_ selection: Selection) { + mutating func mergeIn(_ selection: Selection) { switch selection { case let .field(field): mergeIn(field) case let .inlineFragment(typeCase): mergeIn(typeCase: typeCase) @@ -67,39 +67,43 @@ struct SortedSelections: Equatable, CustomDebugStringConvertible { } } - @inlinable mutating func mergeIn(_ field: Field) { + mutating func mergeIn(_ field: CompilationResult.Field) { + mergeIn(ASTField(field)) + } + + mutating func mergeIn(_ field: Field) { appendOrMerge(field, into: &fields) } - @inlinable mutating func mergeIn(_ fields: T) where T.Element == Field { + mutating func mergeIn(_ fields: T) where T.Element == Field { fields.forEach { mergeIn($0) } } - @inlinable mutating func mergeIn(_ fields: OrderedDictionary) { + mutating func mergeIn(_ fields: OrderedDictionary) { mergeIn(fields.values) } - @inlinable mutating func mergeIn(typeCase: TypeCase) { + mutating func mergeIn(typeCase: TypeCase) { appendOrMerge(typeCase, into: &typeCases) } - @inlinable mutating func mergeIn(typeCases: T) where T.Element == TypeCase { + mutating func mergeIn(typeCases: T) where T.Element == TypeCase { typeCases.forEach { mergeIn(typeCase: $0) } } - @inlinable mutating func mergeIn(typeCases: OrderedDictionary) { + mutating func mergeIn(typeCases: OrderedDictionary) { mergeIn(typeCases: typeCases.values) } - @inlinable mutating func mergeIn(_ fragment: Fragment) { + mutating func mergeIn(_ fragment: Fragment) { fragments[fragment.hashForSelectionSetScope] = fragment } - @inlinable mutating func mergeIn(_ fragments: T) where T.Element == Fragment { + mutating func mergeIn(_ fragments: T) where T.Element == Fragment { fragments.forEach { mergeIn($0) } } - @inlinable mutating func mergeIn(_ fragments: OrderedDictionary) { + mutating func mergeIn(_ fragments: OrderedDictionary) { mergeIn(fragments.values) } diff --git a/SwiftScripts/Package.resolved b/SwiftScripts/Package.resolved index b45fdd9449..3821721d3b 100644 --- a/SwiftScripts/Package.resolved +++ b/SwiftScripts/Package.resolved @@ -100,6 +100,15 @@ "version": "0.3.1" } }, + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections", + "state": { + "branch": null, + "revision": "2d33a0ea89c961dcb2b3da2157963d9c0370347e", + "version": "1.0.1" + } + }, { "package": "llbuild", "repositoryURL": "https://github.com/apple/swift-llbuild.git", diff --git a/SwiftScripts/Sources/Codegen/Target.swift b/SwiftScripts/Sources/Codegen/Target.swift index cfeaf0aff2..4f07747398 100644 --- a/SwiftScripts/Sources/Codegen/Target.swift +++ b/SwiftScripts/Sources/Codegen/Target.swift @@ -83,7 +83,7 @@ enum Target { case .animalKingdom: let graphQLFolderURL = targetRootURL.apollo.childFolderURL(folderName: "graphql") let outputFolderURL = graphQLFolderURL.apollo.childFolderURL(folderName: "Generated") - let schema = try! graphQLFolderURL.apollo.childFileURL(fileName: "schema.graphqls") + let schema = try! graphQLFolderURL.apollo.childFileURL(fileName: "AnimalSchema.graphqls") return ApolloCodegenOptions(codegenEngine: .swift, outputFormat: .multipleFiles(inFolderAtURL: outputFolderURL), diff --git a/Tests/ApolloCodegenTests/AnimalKingdomAPI/AnimalKingdomASTCreationTests.swift b/Tests/ApolloCodegenTests/AnimalKingdomAPI/AnimalKingdomASTCreationTests.swift index 32dedf3abd..3356ac07fd 100644 --- a/Tests/ApolloCodegenTests/AnimalKingdomAPI/AnimalKingdomASTCreationTests.swift +++ b/Tests/ApolloCodegenTests/AnimalKingdomAPI/AnimalKingdomASTCreationTests.swift @@ -154,12 +154,12 @@ final class AnimalKingdomASTCreationTests: XCTestCase { let expected = SortedSelections( fields: [ + .mock("laysEggs", + type: .nonNull(.named(GraphQLScalarType.boolean()))), .mock("bodyTemperature", type: .nonNull(.named(GraphQLScalarType.integer()))), .mock("height", type: .nonNull(.named(GraphQLObjectType.mock("Height")))), - .mock("laysEggs", - type: .nonNull(.named(GraphQLScalarType.boolean()))), .mock("species", type: .nonNull(.named(GraphQLScalarType.string()))), ], @@ -262,18 +262,18 @@ final class AnimalKingdomASTCreationTests: XCTestCase { fields: [ .mock("height", type: .nonNull(.named(GraphQLObjectType.mock("Height")))), - .mock("species", - type: .nonNull(.named(GraphQLScalarType.string()))), - .mock("skinCovering", - type: .named(GraphQLEnumType.skinCovering())), - .mock("predators", - type: .nonNull(.list(.nonNull(.named(GraphQLInterfaceType.mock("Animal")))))), .mock("humanName", type: .named(GraphQLScalarType.string())), .mock("favoriteToy", type: .nonNull(.named(GraphQLScalarType.string()))), .mock("owner", type: .named(GraphQLObjectType.mock("Human"))), + .mock("species", + type: .nonNull(.named(GraphQLScalarType.string()))), + .mock("skinCovering", + type: .named(GraphQLEnumType.skinCovering())), + .mock("predators", + type: .nonNull(.list(.nonNull(.named(GraphQLInterfaceType.mock("Animal")))))), ], typeCases: [ .mock(parentType: GraphQLInterfaceType.mock("WarmBlooded")), @@ -301,7 +301,7 @@ final class AnimalKingdomASTCreationTests: XCTestCase { let height = allAnimalsScope .children.values[1] .mergedSelections - .fields.values[3] + .fields.values[0] let scope = ASTSelectionSet(selectionSet: height.selectionSet!, compilationResult: Self.compilationResult) @@ -343,28 +343,28 @@ final class AnimalKingdomASTCreationTests: XCTestCase { let expected = SortedSelections( fields: [ + .mock("bodyTemperature", + type: .nonNull(.named(GraphQLScalarType.integer()))), .mock("height", type: .nonNull(.named(GraphQLObjectType.mock("Height")))), + .mock("humanName", + type: .named(GraphQLScalarType.string())), + .mock("favoriteToy", + type: .nonNull(.named(GraphQLScalarType.string()))), + .mock("owner", + type: .named(GraphQLObjectType.mock("Human"))), .mock("species", type: .nonNull(.named(GraphQLScalarType.string()))), .mock("skinCovering", type: .named(GraphQLEnumType.skinCovering())), .mock("predators", type: .nonNull(.list(.nonNull(.named(GraphQLInterfaceType.mock("Animal")))))), - .mock("humanName", - type: .named(GraphQLScalarType.string())), - .mock("favoriteToy", - type: .nonNull(.named(GraphQLScalarType.string()))), - .mock("owner", - type: .nonNull(.named(GraphQLObjectType.mock("Human")))), - .mock("bodyTemperature", - type: .nonNull(.named(GraphQLScalarType.integer()))), ], typeCases: [], fragments: [ - .mock("HeightInMeters", type: GraphQLObjectType.mock("Animal")), - .mock("PetDetails", type: GraphQLInterfaceType.mock("Pet")), .mock("WarmBloodedDetails", type: GraphQLInterfaceType.mock("WarmBlooded")), + .mock("PetDetails", type: GraphQLInterfaceType.mock("Pet")), + .mock("HeightInMeters", type: GraphQLInterfaceType.mock("Animal")), ] ) @@ -388,28 +388,28 @@ final class AnimalKingdomASTCreationTests: XCTestCase { fields: [ .mock("isJellicle", type: .nonNull(.named(GraphQLScalarType.boolean()))), + .mock("bodyTemperature", + type: .nonNull(.named(GraphQLScalarType.integer()))), .mock("height", type: .nonNull(.named(GraphQLObjectType.mock("Height")))), + .mock("humanName", + type: .named(GraphQLScalarType.string())), + .mock("favoriteToy", + type: .nonNull(.named(GraphQLScalarType.string()))), + .mock("owner", + type: .named(GraphQLObjectType.mock("Human"))), .mock("species", type: .nonNull(.named(GraphQLScalarType.string()))), .mock("skinCovering", type: .named(GraphQLEnumType.skinCovering())), .mock("predators", type: .nonNull(.list(.nonNull(.named(GraphQLInterfaceType.mock("Animal")))))), - .mock("humanName", - type: .named(GraphQLScalarType.string())), - .mock("favoriteToy", - type: .nonNull(.named(GraphQLScalarType.string()))), - .mock("owner", - type: .nonNull(.named(GraphQLObjectType.mock("Human")))), - .mock("bodyTemperature", - type: .nonNull(.named(GraphQLScalarType.integer()))), ], typeCases: [], fragments: [ - .mock("HeightInMeters", type: GraphQLObjectType.mock("Animal")), .mock("WarmBloodedDetails", type: GraphQLInterfaceType.mock("WarmBlooded")), .mock("PetDetails", type: GraphQLInterfaceType.mock("Pet")), + .mock("HeightInMeters", type: GraphQLInterfaceType.mock("Animal")), ] ) @@ -422,7 +422,7 @@ final class AnimalKingdomASTCreationTests: XCTestCase { } #warning("TODO: This is the same as AllAnimal.AsPet.Height. Should we inherit that object instead?") - func test__mergedSelections_AllAnimalsQuery_AllAnimal_AsCat_Height__isCorrect() { + func test__mergedSelections_AllAnimalsQuery_AllAnimal_AsCat_Height__isCorrect() throws { // given let operation = Self.compilationResult.operations.first { $0.name == "AllAnimalsQuery" } guard case let .field(allAnimals) = operation!.selectionSet.selections[0] else { fail(); return } @@ -431,9 +431,9 @@ final class AnimalKingdomASTCreationTests: XCTestCase { let height = allAnimalsScope .children.values[2] .mergedSelections - .fields.values[1] + .fields.values[2] - let scope = ASTSelectionSet(selectionSet: height.selectionSet!, + let scope = ASTSelectionSet(selectionSet: try XCTUnwrap(height.selectionSet), compilationResult: Self.compilationResult) let expected = SortedSelections( @@ -486,7 +486,7 @@ final class AnimalKingdomASTCreationTests: XCTestCase { .mock(parentType: GraphQLObjectType.mock("Bird")), ], fragments: [ - .mock("HeightInMeters", type: GraphQLObjectType.mock("Animal")), + .mock("HeightInMeters", type: GraphQLInterfaceType.mock("Animal")), ] ) @@ -508,28 +508,28 @@ final class AnimalKingdomASTCreationTests: XCTestCase { let expected = SortedSelections( fields: [ + .mock("wingspan", + type: .nonNull(.named(GraphQLScalarType.integer()))), + .mock("bodyTemperature", + type: .nonNull(.named(GraphQLScalarType.integer()))), .mock("height", type: .nonNull(.named(GraphQLObjectType.mock("Height")))), + .mock("humanName", + type: .named(GraphQLScalarType.string())), + .mock("favoriteToy", + type: .nonNull(.named(GraphQLScalarType.string()))), + .mock("owner", + type: .named(GraphQLObjectType.mock("Human"))), .mock("species", type: .nonNull(.named(GraphQLScalarType.string()))), .mock("skinCovering", type: .named(GraphQLEnumType.skinCovering())), .mock("predators", type: .nonNull(.list(.nonNull(.named(GraphQLInterfaceType.mock("Animal")))))), - .mock("humanName", - type: .named(GraphQLScalarType.string())), - .mock("favoriteToy", - type: .nonNull(.named(GraphQLScalarType.string()))), - .mock("owner", - type: .nonNull(.named(GraphQLObjectType.mock("Human")))), - .mock("bodyTemperature", - type: .nonNull(.named(GraphQLScalarType.integer()))), - .mock("wingspan", - type: .nonNull(.named(GraphQLScalarType.integer()))), ], typeCases: [], fragments: [ - .mock("HeightInMeters", type: GraphQLObjectType.mock("Animal")), + .mock("HeightInMeters", type: GraphQLInterfaceType.mock("Animal")), .mock("WarmBloodedDetails", type: GraphQLInterfaceType.mock("WarmBlooded")), .mock("PetDetails", type: GraphQLInterfaceType.mock("Pet")), ] @@ -544,7 +544,7 @@ final class AnimalKingdomASTCreationTests: XCTestCase { } #warning("TODO: This is the same as AllAnimal.AsPet.Height. Should we inherit that object instead?") - func test__mergedSelections_AllAnimalsQuery_AllAnimal_AsClassroomPet_AsBird_Height__isCorrect() { + func test__mergedSelections_AllAnimalsQuery_AllAnimal_AsClassroomPet_AsBird_Height__isCorrect() throws { // given let operation = Self.compilationResult.operations.first { $0.name == "AllAnimalsQuery" } guard case let .field(allAnimals) = operation!.selectionSet.selections[0] else { fail(); return } @@ -554,9 +554,9 @@ final class AnimalKingdomASTCreationTests: XCTestCase { .children.values[3] .children.values[0] .mergedSelections - .fields.values[1] + .fields.values[2] - let scope = ASTSelectionSet(selectionSet: height.selectionSet!, + let scope = ASTSelectionSet(selectionSet: try XCTUnwrap(height.selectionSet), compilationResult: Self.compilationResult) let expected = SortedSelections( diff --git a/Tests/ApolloCodegenTests/AnimalKingdomAPI/ExpectedGeneratedOutput/Queries/AllAnimalsQuery.swift b/Tests/ApolloCodegenTests/AnimalKingdomAPI/ExpectedGeneratedOutput/Queries/AllAnimalsQuery.swift index 0adb282a1b..406786c3de 100644 --- a/Tests/ApolloCodegenTests/AnimalKingdomAPI/ExpectedGeneratedOutput/Queries/AllAnimalsQuery.swift +++ b/Tests/ApolloCodegenTests/AnimalKingdomAPI/ExpectedGeneratedOutput/Queries/AllAnimalsQuery.swift @@ -133,14 +133,14 @@ public struct AllAnimalsQuery: GraphQLQuery { public static var __parentType: ParentType { .Interface(AnimalKindgomAPI.WarmBlooded.self) } public static var selections: [Selection] { [ - .fragment(WarmBloodedDetails.self), .field("laysEggs", Bool.self), + .fragment(WarmBloodedDetails.self), ] } - public var bodyTemperature: Int { data["bodyTemperature"] } - public var height: WarmBloodedDetails.Height { data["height"] } public var laysEggs: Bool { data["laysEggs"] } public var species: String { data["species"] } + public var bodyTemperature: Int { data["bodyTemperature"] } + public var height: WarmBloodedDetails.Height { data["height"] } public struct Fragments: ResponseObject { public let data: ResponseDict @@ -196,14 +196,14 @@ public struct AllAnimalsQuery: GraphQLQuery { public static var __parentType: ParentType { .Interface(AnimalKindgomAPI.Pet.self) } public static var selections: [Selection] { [ - .fragment(PetDetails.self), .typeCase(AsWarmBlooded.self), + .fragment(PetDetails.self), ] } + public var height: Height { data["height"] } public var humanName: String? { data["humanName"] } public var favoriteToy: String { data["favoriteToy"] } public var owner: PetDetails.Owner? { data["owner"] } - public var height: Height { data["height"] } public var species: String { data["species"] } public var skinCovering: GraphQLEnum? { data["skinCovering"] } public var predators: [Predator] { data["predators"] } @@ -244,22 +244,22 @@ public struct AllAnimalsQuery: GraphQLQuery { public static var __parentType: ParentType { .Interface(AnimalKindgomAPI.WarmBlooded.self) } + public var bodyTemperature: Int { data["bodyTemperature"] } public var height: Height { data["height"] } - public var species: String { data["species"] } - public var skinCovering: GraphQLEnum? { data["skinCovering"] } - public var predators: [Predator] { data["predators"] } public var humanName: String? { data["humanName"] } public var favoriteToy: String { data["favoriteToy"] } public var owner: PetDetails.Owner? { data["owner"] } - public var bodyTemperature: Int { data["bodyTemperature"] } + public var species: String { data["species"] } + public var skinCovering: GraphQLEnum? { data["skinCovering"] } + public var predators: [Predator] { data["predators"] } public struct Fragments: ResponseObject { public let data: ResponseDict public init(data: ResponseDict) { self.data = data } - public var heightInMeters: HeightInMeters { _toFragment() } - public var petDetails: PetDetails { _toFragment() } public var warmBloodedDetails: WarmBloodedDetails { _toFragment() } + public var petDetails: PetDetails { _toFragment() } + public var heightInMeters: HeightInMeters { _toFragment() } } } } @@ -275,22 +275,22 @@ public struct AllAnimalsQuery: GraphQLQuery { ] } public var isJellicle: Bool { data["isJellicle"] } + public var bodyTemperature: Int { data["bodyTemperature"] } public var height: Height { data["height"] } - public var species: String { data["species"] } - public var skinCovering: GraphQLEnum? { data["skinCovering"] } - public var predators: [Predator] { data["predators"] } public var humanName: String? { data["humanName"] } public var favoriteToy: String { data["favoriteToy"] } public var owner: PetDetails.Owner? { data["owner"] } - public var bodyTemperature: Int { data["bodyTemperature"] } + public var species: String { data["species"] } + public var skinCovering: GraphQLEnum? { data["skinCovering"] } + public var predators: [Predator] { data["predators"] } public struct Fragments: ResponseObject { public let data: ResponseDict public init(data: ResponseDict) { self.data = data } - public var heightInMeters: HeightInMeters { _toFragment() } public var warmBloodedDetails: WarmBloodedDetails { _toFragment() } public var petDetails: PetDetails { _toFragment() } + public var heightInMeters: HeightInMeters { _toFragment() } } /// `AllAnimal.AsCat.Height` @@ -344,14 +344,14 @@ public struct AllAnimalsQuery: GraphQLQuery { ] } public var wingspan: Int { data["wingspan"] } + public var bodyTemperature: Int { data["bodyTemperature"] } public var height: Height { data["height"] } - public var species: String { data["species"] } - public var skinCovering: GraphQLEnum? { data["skinCovering"] } - public var predators: [Predator] { data["predators"] } public var humanName: String? { data["humanName"] } public var favoriteToy: String { data["favoriteToy"] } public var owner: PetDetails.Owner? { data["owner"] } - public var bodyTemperature: Int { data["bodyTemperature"] } + public var species: String { data["species"] } + public var skinCovering: GraphQLEnum? { data["skinCovering"] } + public var predators: [Predator] { data["predators"] } public struct Fragments: ResponseObject { public let data: ResponseDict