From d502e944729f3fe288c4609469e146c6bd232e77 Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Mon, 20 May 2024 11:54:48 -0700 Subject: [PATCH] Add @_spi(Execution) to executor for import in test mocks (apollographql/apollo-ios-dev#362) --- .../NetworkResponseExecutionSource.swift | 13 +++++---- Sources/Apollo/FieldSelectionCollector.swift | 13 +++++---- Sources/Apollo/GraphQLExecutionSource.swift | 8 +++--- Sources/Apollo/GraphQLExecutor.swift | 14 ++++++---- Sources/Apollo/GraphQLResultAccumulator.swift | 3 ++- .../Apollo/GraphQLSelectionSetMapper.swift | 27 ++++++++++--------- Sources/Apollo/PossiblyDeferred.swift | 3 ++- Sources/ApolloTestSupport/TestMock.swift | 4 +-- .../TestMockSelectionSetMapper.swift | 2 +- 9 files changed, 51 insertions(+), 36 deletions(-) diff --git a/Sources/Apollo/ExecutionSources/NetworkResponseExecutionSource.swift b/Sources/Apollo/ExecutionSources/NetworkResponseExecutionSource.swift index d2154bbb33..108e492ed3 100644 --- a/Sources/Apollo/ExecutionSources/NetworkResponseExecutionSource.swift +++ b/Sources/Apollo/ExecutionSources/NetworkResponseExecutionSource.swift @@ -4,18 +4,21 @@ import ApolloAPI /// A `GraphQLExecutionSource` configured to execute upon the JSON data from the network response /// for a GraphQL operation. -struct NetworkResponseExecutionSource: GraphQLExecutionSource, CacheKeyComputingExecutionSource { - typealias RawObjectData = JSONObject - typealias FieldCollector = DefaultFieldSelectionCollector +@_spi(Execution) +public struct NetworkResponseExecutionSource: GraphQLExecutionSource, CacheKeyComputingExecutionSource { + public typealias RawObjectData = JSONObject + public typealias FieldCollector = DefaultFieldSelectionCollector - func resolveField( + public init() {} + + public func resolveField( with info: FieldExecutionInfo, on object: JSONObject ) -> PossiblyDeferred { .immediate(.success(object[info.responseKeyForField])) } - func opaqueObjectDataWrapper(for rawData: JSONObject) -> ObjectData { + public func opaqueObjectDataWrapper(for rawData: JSONObject) -> ObjectData { ObjectData(_transformer: DataTransformer(), _rawData: rawData) } diff --git a/Sources/Apollo/FieldSelectionCollector.swift b/Sources/Apollo/FieldSelectionCollector.swift index 47e82e2b32..98b90cfaf1 100644 --- a/Sources/Apollo/FieldSelectionCollector.swift +++ b/Sources/Apollo/FieldSelectionCollector.swift @@ -3,7 +3,8 @@ import Foundation import ApolloAPI #endif -struct FieldSelectionGrouping: Sequence { +@_spi(Execution) +public struct FieldSelectionGrouping: Sequence { private var fieldInfoList: [String: FieldExecutionInfo] = [:] fileprivate(set) var fulfilledFragments: Set = [] @@ -27,7 +28,7 @@ struct FieldSelectionGrouping: Sequence { fulfilledFragments.insert(ObjectIdentifier(type)) } - func makeIterator() -> Dictionary.Iterator { + public func makeIterator() -> Dictionary.Iterator { fieldInfoList.makeIterator() } } @@ -38,7 +39,8 @@ struct FieldSelectionGrouping: Sequence { /// A `FieldSelectionController` is responsible for determining which selections should be executed /// and which fragments are being fulfilled during execution. It does this by adding them to the /// provided `FieldSelectionGrouping`. -protocol FieldSelectionCollector { +@_spi(Execution) +public protocol FieldSelectionCollector { associatedtype ObjectData @@ -57,8 +59,9 @@ protocol FieldSelectionCollector { } -struct DefaultFieldSelectionCollector: FieldSelectionCollector { - static func collectFields( +@_spi(Execution) +public struct DefaultFieldSelectionCollector: FieldSelectionCollector { + static public func collectFields( from selections: [Selection], into groupedFields: inout FieldSelectionGrouping, for object: JSONObject, diff --git a/Sources/Apollo/GraphQLExecutionSource.swift b/Sources/Apollo/GraphQLExecutionSource.swift index 5d613e8faf..6c79f06525 100644 --- a/Sources/Apollo/GraphQLExecutionSource.swift +++ b/Sources/Apollo/GraphQLExecutionSource.swift @@ -8,7 +8,8 @@ import ApolloAPI /// Based on the source of execution data, the way we handle portions of the execution pipeline will /// be different. Each implementation of this protocol provides the necessary implementations for /// executing upon data from a specific source. -protocol GraphQLExecutionSource { +@_spi(Execution) +public protocol GraphQLExecutionSource { /// The type that represents each object in data from the source. associatedtype RawObjectData @@ -43,7 +44,8 @@ protocol GraphQLExecutionSource { /// A type of `GraphQLExecutionSource` that uses the user defined cache key computation /// defined in the ``SchemaConfiguration``. -protocol CacheKeyComputingExecutionSource: GraphQLExecutionSource { +@_spi(Execution) +public protocol CacheKeyComputingExecutionSource: GraphQLExecutionSource { /// A function that should return an `ObjectData` wrapper that performs and custom /// transformations required to transform the raw object data from the source into a consistent /// format to be exposed to the user's ``SchemaConfiguration/cacheKeyInfo(for:object:)`` function. @@ -51,7 +53,7 @@ protocol CacheKeyComputingExecutionSource: GraphQLExecutionSource { } extension CacheKeyComputingExecutionSource { - func computeCacheKey(for object: RawObjectData, in schema: SchemaMetadata.Type) -> CacheKey? { + @_spi(Execution) public func computeCacheKey(for object: RawObjectData, in schema: SchemaMetadata.Type) -> CacheKey? { let dataWrapper = opaqueObjectDataWrapper(for: object) return schema.cacheKey(for: dataWrapper) } diff --git a/Sources/Apollo/GraphQLExecutor.swift b/Sources/Apollo/GraphQLExecutor.swift index 706daa73c1..c52363b756 100644 --- a/Sources/Apollo/GraphQLExecutor.swift +++ b/Sources/Apollo/GraphQLExecutor.swift @@ -3,7 +3,8 @@ import Foundation import ApolloAPI #endif -class ObjectExecutionInfo { +@_spi(Execution) +public class ObjectExecutionInfo { let rootType: any RootSelectionSet.Type let variables: GraphQLOperation.Variables? let schema: SchemaMetadata.Type @@ -58,7 +59,8 @@ class ObjectExecutionInfo { /// /// 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. -class FieldExecutionInfo { +@_spi(Execution) +public class FieldExecutionInfo { let field: Selection.Field let parentInfo: ObjectExecutionInfo @@ -171,17 +173,19 @@ public struct GraphQLExecutionError: Error, LocalizedError { /// The methods in this class closely follow the /// [execution algorithm described in the GraphQL specification] /// (http://spec.graphql.org/draft/#sec-Execution) -final class GraphQLExecutor { +@_spi(Execution) +public final class GraphQLExecutor { private let executionSource: Source - init(executionSource: Source) { + public init(executionSource: Source) { self.executionSource = executionSource } // MARK: - Execution - func execute< + @_spi(Execution) + public func execute< Accumulator: GraphQLResultAccumulator, SelectionSet: RootSelectionSet >( diff --git a/Sources/Apollo/GraphQLResultAccumulator.swift b/Sources/Apollo/GraphQLResultAccumulator.swift index 2ea7934570..2e5c9d00ce 100644 --- a/Sources/Apollo/GraphQLResultAccumulator.swift +++ b/Sources/Apollo/GraphQLResultAccumulator.swift @@ -2,7 +2,8 @@ import ApolloAPI #endif -protocol GraphQLResultAccumulator: AnyObject { +@_spi(Execution) +public protocol GraphQLResultAccumulator: AnyObject { associatedtype PartialResult associatedtype FieldEntry associatedtype ObjectResult diff --git a/Sources/Apollo/GraphQLSelectionSetMapper.swift b/Sources/Apollo/GraphQLSelectionSetMapper.swift index 03d98e100e..84bcdb637b 100644 --- a/Sources/Apollo/GraphQLSelectionSetMapper.swift +++ b/Sources/Apollo/GraphQLSelectionSetMapper.swift @@ -3,13 +3,14 @@ import ApolloAPI #endif /// An accumulator that converts executed data to the correct values to create a `SelectionSet`. -final class GraphQLSelectionSetMapper: GraphQLResultAccumulator { +@_spi(Execution) +public final class GraphQLSelectionSetMapper: GraphQLResultAccumulator { - let requiresCacheKeyComputation: Bool = false + public let requiresCacheKeyComputation: Bool = false let handleMissingValues: HandleMissingValues - enum HandleMissingValues { + public enum HandleMissingValues { case disallow case allowForOptionalFields /// Using this option will result in an unsafe `SelectionSet` that will crash @@ -17,13 +18,13 @@ final class GraphQLSelectionSetMapper: GraphQLResultAccumulator case allowForAllFields } - init( + public init( handleMissingValues: HandleMissingValues = .disallow ) { self.handleMissingValues = handleMissingValues } - func accept(scalar: AnyHashable, info: FieldExecutionInfo) throws -> AnyHashable? { + public func accept(scalar: AnyHashable, info: FieldExecutionInfo) throws -> AnyHashable? { switch info.field.type.namedType { case let .scalar(decodable as any JSONDecodable.Type): // This will convert a JSON value to the expected value type. @@ -33,7 +34,7 @@ final class GraphQLSelectionSetMapper: GraphQLResultAccumulator } } - func accept(customScalar: AnyHashable, info: FieldExecutionInfo) throws -> AnyHashable? { + public func accept(customScalar: AnyHashable, info: FieldExecutionInfo) throws -> AnyHashable? { switch info.field.type.namedType { case let .customScalar(decodable as any JSONDecodable.Type): // This will convert a JSON value to the expected value type, @@ -44,11 +45,11 @@ final class GraphQLSelectionSetMapper: GraphQLResultAccumulator } } - func acceptNullValue(info: FieldExecutionInfo) -> AnyHashable? { + public func acceptNullValue(info: FieldExecutionInfo) -> AnyHashable? { return DataDict._NullValue } - func acceptMissingValue(info: FieldExecutionInfo) throws -> AnyHashable? { + public func acceptMissingValue(info: FieldExecutionInfo) throws -> AnyHashable? { switch handleMissingValues { case .allowForOptionalFields where info.field.type.isNullable: fallthrough case .allowForAllFields: @@ -59,20 +60,20 @@ final class GraphQLSelectionSetMapper: GraphQLResultAccumulator } } - func accept(list: [AnyHashable?], info: FieldExecutionInfo) -> AnyHashable? { + public func accept(list: [AnyHashable?], info: FieldExecutionInfo) -> AnyHashable? { return list } - func accept(childObject: DataDict, info: FieldExecutionInfo) throws -> AnyHashable? { + public func accept(childObject: DataDict, info: FieldExecutionInfo) throws -> AnyHashable? { return childObject } - func accept(fieldEntry: AnyHashable?, info: FieldExecutionInfo) -> (key: String, value: AnyHashable)? { + public func accept(fieldEntry: AnyHashable?, info: FieldExecutionInfo) -> (key: String, value: AnyHashable)? { guard let fieldEntry = fieldEntry else { return nil } return (info.responseKeyForField, fieldEntry) } - func accept( + public func accept( fieldEntries: [(key: String, value: AnyHashable)], info: ObjectExecutionInfo ) throws -> DataDict { @@ -82,7 +83,7 @@ final class GraphQLSelectionSetMapper: GraphQLResultAccumulator ) } - func finish(rootValue: DataDict, info: ObjectExecutionInfo) -> T { + public func finish(rootValue: DataDict, info: ObjectExecutionInfo) -> T { return T.init(_dataDict: rootValue) } } diff --git a/Sources/Apollo/PossiblyDeferred.swift b/Sources/Apollo/PossiblyDeferred.swift index 45d6bd2e0d..4b3f294a2f 100644 --- a/Sources/Apollo/PossiblyDeferred.swift +++ b/Sources/Apollo/PossiblyDeferred.swift @@ -42,7 +42,8 @@ extension Sequence { /// A possibly deferred value that represents either an immediate success or failure value, or a deferred /// value that is evaluated lazily when needed by invoking a throwing closure. -enum PossiblyDeferred { +@_spi(Execution) +public enum PossiblyDeferred { /// An immediate success or failure value, represented as a `Result` instance. case immediate(Result) diff --git a/Sources/ApolloTestSupport/TestMock.swift b/Sources/ApolloTestSupport/TestMock.swift index 8769e3f177..3fa32324f7 100644 --- a/Sources/ApolloTestSupport/TestMock.swift +++ b/Sources/ApolloTestSupport/TestMock.swift @@ -1,7 +1,7 @@ #if !COCOAPODS -@_exported @testable import ApolloAPI +@_exported import ApolloAPI #endif -@testable import Apollo +@_spi(Execution) import Apollo import Foundation @dynamicMemberLookup diff --git a/Sources/ApolloTestSupport/TestMockSelectionSetMapper.swift b/Sources/ApolloTestSupport/TestMockSelectionSetMapper.swift index 37a1912c18..6cd07d3ab7 100644 --- a/Sources/ApolloTestSupport/TestMockSelectionSetMapper.swift +++ b/Sources/ApolloTestSupport/TestMockSelectionSetMapper.swift @@ -1,4 +1,4 @@ -@testable import Apollo +@_spi(Execution) import Apollo import Foundation /// An accumulator that converts data from a `Mock` to the correct values to create a `SelectionSet`.