Skip to content

Commit

Permalink
AST Field Model (#2023)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
AnthonyMDev authored Nov 9, 2021
1 parent b843c2a commit 6d057c7
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 90 deletions.
12 changes: 8 additions & 4 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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 */; };
Expand Down Expand Up @@ -833,7 +834,7 @@
DE223C282720B897004A0148 /* StringInterpolation+NestedIndentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StringInterpolation+NestedIndentation.swift"; sourceTree = "<group>"; };
DE223C2A2721FAD6004A0148 /* ASTMatchers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTMatchers.swift; sourceTree = "<group>"; };
DE223C2C2721FCE8004A0148 /* ScopedSelectionSetHashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScopedSelectionSetHashable.swift; sourceTree = "<group>"; };
DE223C3427223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompilationResult+SelectionSetMerging.swift"; sourceTree = "<group>"; };
DE223C3427223E2A004A0148 /* SelectionMergable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionMergable.swift; sourceTree = "<group>"; };
DE2FCF1C26E806710057EA67 /* SchemaConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaConfiguration.swift; sourceTree = "<group>"; };
DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTransaction.swift; sourceTree = "<group>"; };
DE2FCF2026E807EF0057EA67 /* Cacheable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cacheable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -934,6 +935,7 @@
DEE1B3F3273B08D8007350E5 /* AllAnimals.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllAnimals.graphql.swift; sourceTree = "<group>"; };
DEE1B3F4273B08D8007350E5 /* Types.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.graphql.swift; sourceTree = "<group>"; };
DEE1B3F5273B08D8007350E5 /* WarmBloodedDetails.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarmBloodedDetails.graphql.swift; sourceTree = "<group>"; };
DEFBBC85273470F70088AABC /* ASTField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTField.swift; sourceTree = "<group>"; };
E616B6D026C3335600DB049E /* ExecutionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExecutionTests.swift; sourceTree = "<group>"; };
E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLiteDotSwiftDatabaseBehaviorTests.swift; sourceTree = "<group>"; };
E657CDB926FD01D4005834D6 /* ApolloSchemaInternalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApolloSchemaInternalTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1731,7 +1733,6 @@
9F628EB42593651B00F94F9D /* GraphQLValue.swift */,
9F62E03E2590896400E6E808 /* GraphQLError.swift */,
9F1A9668258F34BB00A06EEB /* CompilationResult.swift */,
DE223C3427223E2A004A0148 /* CompilationResult+SelectionSetMerging.swift */,
9F1A966A258F34BB00A06EEB /* JavaScriptBridge.swift */,
DE3C7973260A646300D2F4FF /* dist */,
);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */,
Expand All @@ -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 */,
Expand Down
74 changes: 74 additions & 0 deletions Sources/ApolloCodegenLib/AST/ASTField.swift
Original file line number Diff line number Diff line change
@@ -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<V>(dynamicMember keyPath: KeyPath<CompilationResult.Field, V>) -> 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
}
}
2 changes: 1 addition & 1 deletion Sources/ApolloCodegenLib/AST/ASTSelectionSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ASTSelectionSet: CustomDebugStringConvertible, Equatable {

let mergedSelectionBuilder: MergedSelectionBuilder

// MARK: - Initialization
// MARK: - Initialization

convenience init(
selectionSet: CompilationResult.SelectionSet,
Expand Down
11 changes: 6 additions & 5 deletions Sources/ApolloCodegenLib/AST/MergedSelectionBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<TypeScope, SortedSelections> = [:]
private(set) var selectionsForScopes: OrderedDictionary<TypeScope, SortedSelections> = [:]
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)
}
}

Expand Down
6 changes: 6 additions & 0 deletions Sources/ApolloCodegenLib/AST/ScopedSelectionSetHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
Expand Down
30 changes: 17 additions & 13 deletions Sources/ApolloCodegenLib/AST/SortedSelections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -47,59 +47,63 @@ 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<T: Sequence>(_ selections: T) where T.Element == Selection {
mutating func mergeIn<T: Sequence>(_ 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)
case let .fragmentSpread(fragment): mergeIn(fragment)
}
}

@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<T: Sequence>(_ fields: T) where T.Element == Field {
mutating func mergeIn<T: Sequence>(_ fields: T) where T.Element == Field {
fields.forEach { mergeIn($0) }
}

@inlinable mutating func mergeIn(_ fields: OrderedDictionary<String, Field>) {
mutating func mergeIn(_ fields: OrderedDictionary<String, Field>) {
mergeIn(fields.values)
}

@inlinable mutating func mergeIn(typeCase: TypeCase) {
mutating func mergeIn(typeCase: TypeCase) {
appendOrMerge(typeCase, into: &typeCases)
}

@inlinable mutating func mergeIn<T: Sequence>(typeCases: T) where T.Element == TypeCase {
mutating func mergeIn<T: Sequence>(typeCases: T) where T.Element == TypeCase {
typeCases.forEach { mergeIn(typeCase: $0) }
}

@inlinable mutating func mergeIn(typeCases: OrderedDictionary<String, TypeCase>) {
mutating func mergeIn(typeCases: OrderedDictionary<String, TypeCase>) {
mergeIn(typeCases: typeCases.values)
}

@inlinable mutating func mergeIn(_ fragment: Fragment) {
mutating func mergeIn(_ fragment: Fragment) {
fragments[fragment.hashForSelectionSetScope] = fragment
}

@inlinable mutating func mergeIn<T: Sequence>(_ fragments: T) where T.Element == Fragment {
mutating func mergeIn<T: Sequence>(_ fragments: T) where T.Element == Fragment {
fragments.forEach { mergeIn($0) }
}

@inlinable mutating func mergeIn(_ fragments: OrderedDictionary<String, Fragment>) {
mutating func mergeIn(_ fragments: OrderedDictionary<String, Fragment>) {
mergeIn(fragments.values)
}

Expand Down
9 changes: 9 additions & 0 deletions SwiftScripts/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion SwiftScripts/Sources/Codegen/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Loading

0 comments on commit 6d057c7

Please sign in to comment.