From a3e450e37a9c2fffffd9718e14827fd3cf7b4f55 Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Thu, 9 Sep 2021 12:25:56 -0700 Subject: [PATCH] ExecutionInfo -> ObjectExecutionInfo --- Sources/Apollo/GraphQLDependencyTracker.swift | 2 +- Sources/Apollo/GraphQLExecutor.swift | 69 ++++++++++--------- Sources/Apollo/GraphQLResponseGenerator.swift | 2 +- Sources/Apollo/GraphQLResultAccumulator.swift | 48 +++++++++---- Sources/Apollo/GraphQLResultNormalizer.swift | 2 +- .../Apollo/GraphQLSelectionSetMapper.swift | 2 +- 6 files changed, 72 insertions(+), 53 deletions(-) diff --git a/Sources/Apollo/GraphQLDependencyTracker.swift b/Sources/Apollo/GraphQLDependencyTracker.swift index f524e689c4..b01b630b29 100644 --- a/Sources/Apollo/GraphQLDependencyTracker.swift +++ b/Sources/Apollo/GraphQLDependencyTracker.swift @@ -17,7 +17,7 @@ final class GraphQLDependencyTracker: GraphQLResultAccumulator { dependentKeys.insert(info.cachePath.joined) } - func accept(fieldEntries: [Void], info: FieldExecutionInfo) { + func accept(fieldEntries: [Void], info: ObjectExecutionInfo) { } func finish(rootValue: Void) -> Set { diff --git a/Sources/Apollo/GraphQLExecutor.swift b/Sources/Apollo/GraphQLExecutor.swift index 14d8bf2ecd..b2478519ac 100644 --- a/Sources/Apollo/GraphQLExecutor.swift +++ b/Sources/Apollo/GraphQLExecutor.swift @@ -33,7 +33,7 @@ struct GraphQLResolveInfo { } } -fileprivate struct ExecutionInfo { // TODO: Rename? +struct ObjectExecutionInfo { let variables: [String: JSONEncodable]? private(set) var responsePath: ResponsePath = [] private(set) var cachePath: ResponsePath = [] @@ -60,33 +60,13 @@ fileprivate struct ExecutionInfo { // TODO: Rename? } } -fileprivate struct FieldSelectionGrouping: Sequence { - private var fieldInfoList: [String: FieldExecutionInfo] = [:] - - var count: Int { fieldInfoList.count } - - mutating func append(field: Selection.Field, withInfo info: ExecutionInfo) { - let fieldKey = field.responseKey - if var fieldInfo = fieldInfoList[fieldKey] { - fieldInfo.mergedFields.append(field) - fieldInfoList[fieldKey] = fieldInfo - } else { - fieldInfoList[fieldKey] = FieldExecutionInfo(field: field, info: info) - } - } - - func makeIterator() -> Dictionary.Iterator { - fieldInfoList.makeIterator() - } -} - /// Stores the information for executing a field and all duplicate fields on the same selection set. /// /// GraphQL validation makes sure all fields sharing the same response key have the same /// arguments and are of the same type, so we only need to resolve one field. struct FieldExecutionInfo { let field: Selection.Field - private let info: ExecutionInfo + private let parentInfo: ObjectExecutionInfo var mergedFields: [Selection.Field] @@ -98,20 +78,20 @@ struct FieldExecutionInfo { fileprivate init( field: Selection.Field, - info: ExecutionInfo + parentInfo: ObjectExecutionInfo ) { self.field = field - self.info = info + self.parentInfo = parentInfo mergedFields = [field] let responseKey = field.responseKey - responsePath = info.responsePath.appending(responseKey) + responsePath = parentInfo.responsePath.appending(responseKey) responseKeyForField = responseKey } fileprivate mutating func computeCacheKeyAndPath() throws { - let cacheKey = try field.cacheKey(with: info.variables) - cachePath = info.cachePath.appending(cacheKey) + let cacheKey = try field.cacheKey(with: parentInfo.variables) + cachePath = parentInfo.cachePath.appending(cacheKey) cacheKeyForField = cacheKey } @@ -130,13 +110,33 @@ struct FieldExecutionInfo { } /// Returns the `ExecutionInfo` that should be used for executing the child selections. - fileprivate func executionInfoForChildSelections() -> ExecutionInfo { - return ExecutionInfo(variables: info.variables, + fileprivate func executionInfoForChildSelections() -> ObjectExecutionInfo { + return ObjectExecutionInfo(variables: parentInfo.variables, responsePath: responsePath, cachePath: cachePath) } } +fileprivate struct FieldSelectionGrouping: Sequence { + private var fieldInfoList: [String: FieldExecutionInfo] = [:] + + var count: Int { fieldInfoList.count } + + mutating func append(field: Selection.Field, withInfo info: ObjectExecutionInfo) { + let fieldKey = field.responseKey + if var fieldInfo = fieldInfoList[fieldKey] { + fieldInfo.mergedFields.append(field) + fieldInfoList[fieldKey] = fieldInfo + } else { + fieldInfoList[fieldKey] = FieldExecutionInfo(field: field, parentInfo: info) + } + } + + func makeIterator() -> Dictionary.Iterator { + fieldInfoList.makeIterator() + } +} + /// An error which has occurred in processing a GraphQLResult public struct GraphQLResultError: Error, LocalizedError { let path: ResponsePath @@ -491,7 +491,7 @@ final class GraphQLExecutor { variables: [String: JSONEncodable]? = nil, accumulator: Accumulator ) throws -> Accumulator.FinalResult { - let info = ExecutionInfo(variables: variables, rootCacheKey: key) + let info = ObjectExecutionInfo(variables: variables, rootCacheKey: key) let rootValue = execute(selections: selections, on: object, @@ -504,7 +504,7 @@ final class GraphQLExecutor { private func execute( selections: [Selection], on object: JSONObject, - info: ExecutionInfo, + info: ObjectExecutionInfo, accumulator: Accumulator ) -> PossiblyDeferred { do { @@ -539,7 +539,7 @@ final class GraphQLExecutor { private func groupFields( _ selections: [Selection], on object: JSONObject, - info: ExecutionInfo + info: ObjectExecutionInfo ) throws -> FieldSelectionGrouping { var grouping = FieldSelectionGrouping() try groupFields(selections, @@ -559,7 +559,7 @@ final class GraphQLExecutor { _ selections: [Selection], forRuntimeType runtimeType: Object.Type?, into groupedFields: inout FieldSelectionGrouping, - info: ExecutionInfo + info: ObjectExecutionInfo ) throws { for selection in selections { switch selection { @@ -742,7 +742,8 @@ final class GraphQLExecutor { private func executeChildSelections( forObjectTypeFields fieldInfo: FieldExecutionInfo, onChildObject object: JSONObject, - accumulator: Accumulator) -> PossiblyDeferred { + accumulator: Accumulator + ) -> PossiblyDeferred { let selections = fieldInfo.computeChildSelections() var childExecutionInfo = fieldInfo.executionInfoForChildSelections() diff --git a/Sources/Apollo/GraphQLResponseGenerator.swift b/Sources/Apollo/GraphQLResponseGenerator.swift index 10f7389a20..d603c56640 100644 --- a/Sources/Apollo/GraphQLResponseGenerator.swift +++ b/Sources/Apollo/GraphQLResponseGenerator.swift @@ -17,7 +17,7 @@ final class GraphQLResponseGenerator: GraphQLResultAccumulator { return (info.responseKeyForField, fieldEntry) } - func accept(fieldEntries: [(key: String, value: JSONValue)], info: FieldExecutionInfo) -> JSONValue { + func accept(fieldEntries: [(key: String, value: JSONValue)], info: ObjectExecutionInfo) -> JSONValue { return JSONObject(fieldEntries, uniquingKeysWith: { (_, last) in last }) } diff --git a/Sources/Apollo/GraphQLResultAccumulator.swift b/Sources/Apollo/GraphQLResultAccumulator.swift index b956d674ca..f375bc0db9 100644 --- a/Sources/Apollo/GraphQLResultAccumulator.swift +++ b/Sources/Apollo/GraphQLResultAccumulator.swift @@ -9,7 +9,7 @@ protocol GraphQLResultAccumulator: AnyObject { func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry - func accept(fieldEntries: [FieldEntry], info: FieldExecutionInfo) throws -> ObjectResult + func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult func finish(rootValue: ObjectResult) throws -> FinalResult } @@ -37,29 +37,35 @@ final class Zip2Accumulator PartialResult { - return (try accumulator1.accept(scalar: scalar, info: info), try accumulator2.accept(scalar: scalar, info: info)) + return (try accumulator1.accept(scalar: scalar, info: info), + try accumulator2.accept(scalar: scalar, info: info)) } func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { - return (try accumulator1.acceptNullValue(info: info), try accumulator2.acceptNullValue(info: info)) + return (try accumulator1.acceptNullValue(info: info), + try accumulator2.acceptNullValue(info: info)) } func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { let (list1, list2) = unzip(list) - return (try accumulator1.accept(list: list1, info: info), try accumulator2.accept(list: list2, info: info)) + return (try accumulator1.accept(list: list1, info: info), + try accumulator2.accept(list: list2, info: info)) } func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry { - return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), try accumulator2.accept(fieldEntry: fieldEntry.1, info: info)) + return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), + try accumulator2.accept(fieldEntry: fieldEntry.1, info: info)) } - func accept(fieldEntries: [FieldEntry], info: FieldExecutionInfo) throws -> ObjectResult { + func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { let (fieldEntries1, fieldEntries2) = unzip(fieldEntries) - return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), try accumulator2.accept(fieldEntries: fieldEntries2, info: info)) + return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), + try accumulator2.accept(fieldEntries: fieldEntries2, info: info)) } func finish(rootValue: ObjectResult) throws -> FinalResult { - return (try accumulator1.finish(rootValue: rootValue.0), try accumulator2.finish(rootValue: rootValue.1)) + return (try accumulator1.finish(rootValue: rootValue.0), + try accumulator2.finish(rootValue: rootValue.1)) } } @@ -83,28 +89,40 @@ final class Zip3Accumulator PartialResult { - return (try accumulator1.accept(scalar: scalar, info: info), try accumulator2.accept(scalar: scalar, info: info), try accumulator3.accept(scalar: scalar, info: info)) + return (try accumulator1.accept(scalar: scalar, info: info), + try accumulator2.accept(scalar: scalar, info: info), + try accumulator3.accept(scalar: scalar, info: info)) } func acceptNullValue(info: FieldExecutionInfo) throws -> PartialResult { - return (try accumulator1.acceptNullValue(info: info), try accumulator2.acceptNullValue(info: info), try accumulator3.acceptNullValue(info: info)) + return (try accumulator1.acceptNullValue(info: info), + try accumulator2.acceptNullValue(info: info), + try accumulator3.acceptNullValue(info: info)) } func accept(list: [PartialResult], info: FieldExecutionInfo) throws -> PartialResult { let (list1, list2, list3) = unzip(list) - return (try accumulator1.accept(list: list1, info: info), try accumulator2.accept(list: list2, info: info), try accumulator3.accept(list: list3, info: info)) + return (try accumulator1.accept(list: list1, info: info), + try accumulator2.accept(list: list2, info: info), + try accumulator3.accept(list: list3, info: info)) } func accept(fieldEntry: PartialResult, info: FieldExecutionInfo) throws -> FieldEntry { - return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), try accumulator2.accept(fieldEntry: fieldEntry.1, info: info), try accumulator3.accept(fieldEntry: fieldEntry.2, info: info)) + return (try accumulator1.accept(fieldEntry: fieldEntry.0, info: info), + try accumulator2.accept(fieldEntry: fieldEntry.1, info: info), + try accumulator3.accept(fieldEntry: fieldEntry.2, info: info)) } - func accept(fieldEntries: [FieldEntry], info: FieldExecutionInfo) throws -> ObjectResult { + func accept(fieldEntries: [FieldEntry], info: ObjectExecutionInfo) throws -> ObjectResult { let (fieldEntries1, fieldEntries2, fieldEntries3) = unzip(fieldEntries) - return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), try accumulator2.accept(fieldEntries: fieldEntries2, info: info), try accumulator3.accept(fieldEntries: fieldEntries3, info: info)) + return (try accumulator1.accept(fieldEntries: fieldEntries1, info: info), + try accumulator2.accept(fieldEntries: fieldEntries2, info: info), + try accumulator3.accept(fieldEntries: fieldEntries3, info: info)) } func finish(rootValue: ObjectResult) throws -> FinalResult { - return (try accumulator1.finish(rootValue: rootValue.0), try accumulator2.finish(rootValue: rootValue.1), try accumulator3.finish(rootValue: rootValue.2)) + return (try accumulator1.finish(rootValue: rootValue.0), + try accumulator2.finish(rootValue: rootValue.1), + try accumulator3.finish(rootValue: rootValue.2)) } } diff --git a/Sources/Apollo/GraphQLResultNormalizer.swift b/Sources/Apollo/GraphQLResultNormalizer.swift index ac869631c0..320636a272 100644 --- a/Sources/Apollo/GraphQLResultNormalizer.swift +++ b/Sources/Apollo/GraphQLResultNormalizer.swift @@ -19,7 +19,7 @@ final class GraphQLResultNormalizer: GraphQLResultAccumulator { return (info.cacheKeyForField, fieldEntry) } - func accept(fieldEntries: [(key: String, value: JSONValue)], info: FieldExecutionInfo) throws -> JSONValue { + func accept(fieldEntries: [(key: String, value: JSONValue)], info: ObjectExecutionInfo) throws -> JSONValue { let cachePath = info.cachePath.joined let object = JSONObject(fieldEntries, uniquingKeysWith: { (_, last) in last }) diff --git a/Sources/Apollo/GraphQLSelectionSetMapper.swift b/Sources/Apollo/GraphQLSelectionSetMapper.swift index 7508112b45..5ac189c625 100644 --- a/Sources/Apollo/GraphQLSelectionSetMapper.swift +++ b/Sources/Apollo/GraphQLSelectionSetMapper.swift @@ -21,7 +21,7 @@ final class GraphQLSelectionSetMapper: GraphQLRes return (info.responseKeyForField, fieldEntry) } - func accept(fieldEntries: [(key: String, value: Any?)], info: FieldExecutionInfo) throws -> ResponseDict { + func accept(fieldEntries: [(key: String, value: Any?)], info: ObjectExecutionInfo) throws -> ResponseDict { return ResponseDict(data: .init(fieldEntries, uniquingKeysWith: { (_, last) in last })) }