Skip to content

Commit

Permalink
Move handling of missing values from Executor to Field Resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyMDev committed Dec 15, 2022
1 parent 90c0db8 commit 9927eae
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 18 deletions.
27 changes: 18 additions & 9 deletions Sources/Apollo/ApolloStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,17 @@ public class ApolloStore {
fileprivate let cache: NormalizedCache

fileprivate lazy var loader: DataLoader<CacheKey, Record> = DataLoader(self.cache.loadRecords)
fileprivate lazy var executor = GraphQLExecutor { object, info in
return object[info.cacheKeyForField]
} resolveReference: { [weak self] reference in
guard let self = self else {
return .immediate(.failure(ApolloStore.Error.notWithinReadTransaction))
}
return self.loadObject(forKey: reference.key)
fileprivate lazy var readExecutor = GraphQLExecutor { object, info in
guard let value = object[info.cacheKeyForField] else {
throw JSONDecodingError.missingValue
}
return value
} resolveReference: { [weak self] reference in
guard let self = self else {
return .immediate(.failure(ApolloStore.Error.notWithinReadTransaction))
}
return self.loadObject(forKey: reference.key)
}

fileprivate init(store: ApolloStore) {
self.cache = store.cache
Expand Down Expand Up @@ -226,7 +229,7 @@ public class ApolloStore {
) throws -> Accumulator.FinalResult {
let object = try loadObject(forKey: key).get()

return try executor.execute(
return try readExecutor.execute(
selectionSet: type,
on: object,
withRootCacheReference: CacheReference(key),
Expand Down Expand Up @@ -293,7 +296,13 @@ public class ApolloStore {
) throws {
let normalizer = GraphQLResultNormalizer()
let executor = GraphQLExecutor { object, info in
return object[info.responseKeyForField]
guard let value = object[info.responseKeyForField] else {
guard info.field.type.isNullable else {
throw JSONDecodingError.missingValue
}
return NSNull()
}
return value
}

let records = try executor.execute(
Expand Down
14 changes: 8 additions & 6 deletions Sources/Apollo/GraphQLExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import Foundation
import ApolloAPI
#endif

/// A field resolver is responsible for resolving a value for a field.
typealias GraphQLFieldResolver = (_ object: JSONObject, _ info: FieldExecutionInfo) -> JSONValue?
/// A field resolver is responsible for resolving a value for a field from a JSON object.
///
/// Because the field resolver should have context on the source of JSON object, it is responsible
/// for knowing how to handle missing fields. It must either return a valid value or throw an error.
/// If the object has no value for the field, the resolver can return `NSNull` or throw a
/// `JSONDecodingError`.
typealias GraphQLFieldResolver = (_ object: JSONObject, _ info: FieldExecutionInfo) throws -> JSONValue

/// A reference resolver is responsible for resolving an object based on its key. These references are
/// used in normalized records, and data for these objects has to be loaded from the cache for execution to continue.
Expand Down Expand Up @@ -308,10 +313,7 @@ final class GraphQLExecutor {
}

return PossiblyDeferred {
guard let value = fieldResolver(object, fieldInfo) else {
throw JSONDecodingError.missingValue
}
return value
try fieldResolver(object, fieldInfo)
}.flatMap {
return self.complete(fields: fieldInfo,
withValue: $0,
Expand Down
5 changes: 4 additions & 1 deletion Sources/Apollo/GraphQLResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ public final class GraphQLResponse<Data: RootSelectionSet> {
}

let executor = GraphQLExecutor { object, info in
return object[info.responseKeyForField]
guard let value = object[info.responseKeyForField] else {
throw JSONDecodingError.missingValue
}
return value
}

executor.shouldComputeCachePath = computeCachePaths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase {

private static let executor: GraphQLExecutor = {
let executor = GraphQLExecutor { object, info in
return object[info.responseKeyForField]
guard let value = object[info.responseKeyForField] else {
throw JSONDecodingError.missingValue
}
return value
}

executor.shouldComputeCachePath = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase {

private static let executor: GraphQLExecutor = {
let executor = GraphQLExecutor { object, info in
return object[info.responseKeyForField]
guard let value = object[info.responseKeyForField] else {
throw JSONDecodingError.missingValue
}
return value
}
executor.shouldComputeCachePath = false
return executor
Expand Down

0 comments on commit 9927eae

Please sign in to comment.