Skip to content

Commit

Permalink
Add @_spi(Execution) to executor for import in test mocks (apollograp…
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyMDev authored and gh-action-runner committed May 20, 2024
1 parent da19e5e commit d502e94
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<AnyHashable?> {
.immediate(.success(object[info.responseKeyForField]))
}

func opaqueObjectDataWrapper(for rawData: JSONObject) -> ObjectData {
public func opaqueObjectDataWrapper(for rawData: JSONObject) -> ObjectData {
ObjectData(_transformer: DataTransformer(), _rawData: rawData)
}

Expand Down
13 changes: 8 additions & 5 deletions Sources/Apollo/FieldSelectionCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<ObjectIdentifier> = []

Expand All @@ -27,7 +28,7 @@ struct FieldSelectionGrouping: Sequence {
fulfilledFragments.insert(ObjectIdentifier(type))
}

func makeIterator() -> Dictionary<String, FieldExecutionInfo>.Iterator {
public func makeIterator() -> Dictionary<String, FieldExecutionInfo>.Iterator {
fieldInfoList.makeIterator()
}
}
Expand All @@ -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<ObjectData> {
@_spi(Execution)
public protocol FieldSelectionCollector<ObjectData> {

associatedtype ObjectData

Expand All @@ -57,8 +59,9 @@ protocol FieldSelectionCollector<ObjectData> {

}

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,
Expand Down
8 changes: 5 additions & 3 deletions Sources/Apollo/GraphQLExecutionSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -43,15 +44,16 @@ 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.
func opaqueObjectDataWrapper(for: RawObjectData) -> ObjectData
}

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)
}
Expand Down
14 changes: 9 additions & 5 deletions Sources/Apollo/GraphQLExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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<Source: GraphQLExecutionSource> {
@_spi(Execution)
public final class GraphQLExecutor<Source: GraphQLExecutionSource> {

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
>(
Expand Down
3 changes: 2 additions & 1 deletion Sources/Apollo/GraphQLResultAccumulator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import ApolloAPI
#endif

protocol GraphQLResultAccumulator: AnyObject {
@_spi(Execution)
public protocol GraphQLResultAccumulator: AnyObject {
associatedtype PartialResult
associatedtype FieldEntry
associatedtype ObjectResult
Expand Down
27 changes: 14 additions & 13 deletions Sources/Apollo/GraphQLSelectionSetMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@ import ApolloAPI
#endif

/// An accumulator that converts executed data to the correct values to create a `SelectionSet`.
final class GraphQLSelectionSetMapper<T: SelectionSet>: GraphQLResultAccumulator {
@_spi(Execution)
public final class GraphQLSelectionSetMapper<T: SelectionSet>: 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
/// when a required field that has missing data is accessed.
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.
Expand All @@ -33,7 +34,7 @@ final class GraphQLSelectionSetMapper<T: SelectionSet>: 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,
Expand All @@ -44,11 +45,11 @@ final class GraphQLSelectionSetMapper<T: SelectionSet>: 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:
Expand All @@ -59,20 +60,20 @@ final class GraphQLSelectionSetMapper<T: SelectionSet>: 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 {
Expand All @@ -82,7 +83,7 @@ final class GraphQLSelectionSetMapper<T: SelectionSet>: GraphQLResultAccumulator
)
}

func finish(rootValue: DataDict, info: ObjectExecutionInfo) -> T {
public func finish(rootValue: DataDict, info: ObjectExecutionInfo) -> T {
return T.init(_dataDict: rootValue)
}
}
3 changes: 2 additions & 1 deletion Sources/Apollo/PossiblyDeferred.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value> {
@_spi(Execution)
public enum PossiblyDeferred<Value> {
/// An immediate success or failure value, represented as a `Result` instance.
case immediate(Result<Value, Error>)

Expand Down
4 changes: 2 additions & 2 deletions Sources/ApolloTestSupport/TestMock.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#if !COCOAPODS
@_exported @testable import ApolloAPI
@_exported import ApolloAPI
#endif
@testable import Apollo
@_spi(Execution) import Apollo
import Foundation

@dynamicMemberLookup
Expand Down
2 changes: 1 addition & 1 deletion Sources/ApolloTestSupport/TestMockSelectionSetMapper.swift
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down

0 comments on commit d502e94

Please sign in to comment.