Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variables on GraphQLOperation - Refactor #1963

Merged
merged 6 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
DE05860B266978A100265760 /* SelectionSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B10260A6FC900D2F4FF /* SelectionSet.swift */; };
DE05860C266978A100265760 /* FragmentProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B12260A6FC900D2F4FF /* FragmentProtocols.swift */; };
DE05860D266978A100265760 /* ScalarTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B15260A6FCA00D2F4FF /* ScalarTypes.swift */; };
DE05860E266978A100265760 /* GraphQLOptional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B68F06E241C649E00E97318 /* GraphQLOptional.swift */; };
DE05860E266978A100265760 /* GraphQLNullable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B68F06E241C649E00E97318 /* GraphQLNullable.swift */; };
DE058610266978A100265760 /* InputValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC9A9C11E2D3CAF0023C4D5 /* InputValue.swift */; };
DE058616266978A100265760 /* GraphQLEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3C7B14260A6FCA00D2F4FF /* GraphQLEnum.swift */; };
DE05862D2669800000265760 /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE071AE2368D34D00FA5952 /* Matchable.swift */; };
Expand Down Expand Up @@ -226,7 +226,6 @@
DE674D9D261CEEE4000E8FC8 /* c.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9B2061172591B3550020D1E0 /* c.txt */; };
DE674D9E261CEEE4000E8FC8 /* b.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9B2061182591B3550020D1E0 /* b.txt */; };
DE674D9F261CEEE4000E8FC8 /* a.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9B2061192591B3550020D1E0 /* a.txt */; };
DE6B156A261505660068D642 /* GraphQLMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6B154A261505450068D642 /* GraphQLMap.swift */; };
DE6B15AF26152BE10068D642 /* DefaultInterceptorProviderIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6B15AE26152BE10068D642 /* DefaultInterceptorProviderIntegrationTests.swift */; };
DE6B15B126152BE10068D642 /* Apollo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; };
DE736F4626FA6EE6007187F2 /* InflectorKit in Frameworks */ = {isa = PBXBuildFile; productRef = E6E4209126A7DF4200B82624 /* InflectorKit */; };
Expand Down Expand Up @@ -627,7 +626,7 @@
9B68353E2463481A00337AE6 /* ApolloUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ApolloUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9B68354A2463498D00337AE6 /* Apollo-Target-ApolloUtils.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Apollo-Target-ApolloUtils.xcconfig"; sourceTree = "<group>"; };
9B68F0542416B33300E97318 /* LineByLineComparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineByLineComparison.swift; sourceTree = "<group>"; };
9B68F06E241C649E00E97318 /* GraphQLOptional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLOptional.swift; sourceTree = "<group>"; };
9B68F06E241C649E00E97318 /* GraphQLNullable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLNullable.swift; sourceTree = "<group>"; };
9B6CB23D238077B60007259D /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
9B708AAC2305884500604A11 /* ApolloClientProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloClientProtocol.swift; sourceTree = "<group>"; };
9B74BCBE2333F4ED00508F84 /* run-bundled-codegen.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = "run-bundled-codegen.sh"; path = "scripts/run-bundled-codegen.sh"; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -853,7 +852,6 @@
DE5EB9C626EFE0F80004176A /* JSONValueMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONValueMatcher.swift; sourceTree = "<group>"; };
DE5EB9CA26EFE5510004176A /* MockOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOperation.swift; sourceTree = "<group>"; };
DE664ED326602AF60054DB4F /* Selection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selection.swift; sourceTree = "<group>"; };
DE6B154A261505450068D642 /* GraphQLMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLMap.swift; sourceTree = "<group>"; };
DE6B15AC26152BE10068D642 /* ApolloServerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ApolloServerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DE6B15AE26152BE10068D642 /* DefaultInterceptorProviderIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultInterceptorProviderIntegrationTests.swift; sourceTree = "<group>"; };
DE6B15B026152BE10068D642 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1492,7 +1490,6 @@
children = (
DE9C04AB26EA763E00EC35E7 /* Accumulators */,
9FF90A5C1DDDEB100034C3B6 /* GraphQLExecutor.swift */,
DE6B154A261505450068D642 /* GraphQLMap.swift */,
9FA6F3671E65DF4700BF8D73 /* GraphQLResultAccumulator.swift */,
9F7BA89822927A3700999B3B /* ResponsePath.swift */,
DE0586322669948500265760 /* InputValue+Evaluation.swift */,
Expand Down Expand Up @@ -1729,7 +1726,7 @@
DE2FCF2226E8082A0057EA67 /* SchemaTypes */,
DE2FCF1E26E807CC0057EA67 /* CacheTransaction.swift */,
DE9C04AE26EAAEE800EC35E7 /* CacheReference.swift */,
9B68F06E241C649E00E97318 /* GraphQLOptional.swift */,
9B68F06E241C649E00E97318 /* GraphQLNullable.swift */,
DE3C7B12260A6FC900D2F4FF /* FragmentProtocols.swift */,
DE3C7B14260A6FCA00D2F4FF /* GraphQLEnum.swift */,
DE3C7B11260A6FC900D2F4FF /* ResponseDict.swift */,
Expand Down Expand Up @@ -2828,7 +2825,6 @@
9FEB050D1DB5732300DA3B44 /* JSONSerializationFormat.swift in Sources */,
9B260BEB245A020300562176 /* ApolloInterceptor.swift in Sources */,
54DDB0921EA045870009DD99 /* InMemoryNormalizedCache.swift in Sources */,
DE6B156A261505660068D642 /* GraphQLMap.swift in Sources */,
9B554CC4247DC29A002F452A /* TaskData.swift in Sources */,
9B9BBAF524DB4F890021C30F /* AutomaticPersistedQueryInterceptor.swift in Sources */,
9BA1244A22D8A8EA00BF1D24 /* JSONSerialization+Sorting.swift in Sources */,
Expand Down Expand Up @@ -2946,7 +2942,7 @@
DE2FCF2A26E8083A0057EA67 /* Field.swift in Sources */,
DE05860D266978A100265760 /* ScalarTypes.swift in Sources */,
DE9C04AF26EAAEE800EC35E7 /* CacheReference.swift in Sources */,
DE05860E266978A100265760 /* GraphQLOptional.swift in Sources */,
DE05860E266978A100265760 /* GraphQLNullable.swift in Sources */,
DECD53CF26EC0EE50059A639 /* OutputTypeConvertible.swift in Sources */,
DE2FCF2126E807EF0057EA67 /* Cacheable.swift in Sources */,
DEA6A83426F298660091AF8A /* ParentType.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions Sources/Apollo/ApolloStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public class ApolloStore {
public func readObject<SelectionSet: RootSelectionSet>(
ofType type: SelectionSet.Type,
withKey key: CacheKey,
variables: [String: InputValue]? = nil
variables: GraphQLOperation.Variables? = nil
) throws -> SelectionSet {
return try self.readObject(ofType: type,
withKey: key,
Expand All @@ -195,7 +195,7 @@ public class ApolloStore {
func readObject<SelectionSet: RootSelectionSet, Accumulator: GraphQLResultAccumulator>(
ofType type: SelectionSet.Type,
withKey key: CacheKey,
variables: [String: InputValue]? = nil,
variables: GraphQLOperation.Variables? = nil,
accumulator: Accumulator
) throws -> Accumulator.FinalResult {
let object = try loadObject(forKey: key).get()
Expand Down
11 changes: 5 additions & 6 deletions Sources/Apollo/GraphQLExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ typealias GraphQLFieldResolver = (_ object: JSONObject, _ info: FieldExecutionIn
typealias ReferenceResolver = (CacheReference) -> PossiblyDeferred<JSONObject>

struct ObjectExecutionInfo {
let variables: [String: InputValue]?
let variables: GraphQLOperation.Variables?
let schema: SchemaConfiguration.Type
private(set) var responsePath: ResponsePath = []
private(set) var cachePath: ResponsePath = []

fileprivate init(
variables: [String: InputValue]?,
variables: GraphQLOperation.Variables?,
schema: SchemaConfiguration.Type,
responsePath: ResponsePath,
cachePath: ResponsePath
Expand All @@ -33,7 +33,7 @@ struct ObjectExecutionInfo {
}

fileprivate init(
variables: [String: InputValue]?,
variables: GraphQLOperation.Variables?,
schema: SchemaConfiguration.Type,
withRootCacheReference root: CacheReference? = nil
) {
Expand Down Expand Up @@ -173,7 +173,7 @@ final class GraphQLExecutor {
selectionSet: RootSelectionSet.Type,
on data: JSONObject,
withRootCacheReference root: CacheReference? = nil,
variables: [String: InputValue]? = nil,
variables: GraphQLOperation.Variables? = nil,
accumulator: Accumulator
) throws -> Accumulator.FinalResult {
let info = ObjectExecutionInfo(variables: variables,
Expand Down Expand Up @@ -247,8 +247,7 @@ final class GraphQLExecutor {
groupedFields.append(field: field, withInfo: info)

case let .booleanCondition(booleanCondition):
guard case let .scalar(boolValue as Bool) =
info.variables?[booleanCondition.variableName] else {
guard let boolValue = info.variables?[booleanCondition.variableName] as? Bool else {
throw GraphQLError("Variable \"\(booleanCondition.variableName)\" was not provided.")
}
if boolValue == !booleanCondition.inverted {
Expand Down
11 changes: 6 additions & 5 deletions Sources/Apollo/GraphQLGETTransformer.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import Foundation
#if !COCOAPODS
import ApolloUtils
import ApolloAPI
#endif

public struct GraphQLGETTransformer {

let body: GraphQLMap
let body: JSONEncodableDictionary
let url: URL

/// A helper for transforming a GraphQLMap that can be sent with a `POST` request into a URL with query parameters for a `GET` request.
/// A helper for transforming a `JSONEncodableDictionary` that can be sent with a `POST` request into a URL with query parameters for a `GET` request.
///
/// - Parameters:
/// - body: The GraphQLMap to transform from the body of a `POST` request
/// - body: The `JSONEncodableDictionary` to transform from the body of a `POST` request
/// - url: The base url to append the query to.
public init(body: GraphQLMap, url: URL) {
public init(body: JSONEncodableDictionary, url: URL) {
self.body = body
self.url = url
}
Expand All @@ -30,7 +31,7 @@ public struct GraphQLGETTransformer {

do {
_ = try self.body.sorted(by: {$0.key < $1.key}).compactMap({ arg in
if let value = arg.value as? GraphQLMap {
if let value = arg.value as? JSONEncodableDictionary {
let data = try JSONSerialization.sortedData(withJSONObject: value.jsonValue)
if let string = String(data: data, encoding: .utf8) {
queryItems.append(URLQueryItem(name: arg.key, value: string))
Expand Down
22 changes: 0 additions & 22 deletions Sources/Apollo/GraphQLMap.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/Apollo/GraphQLResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public final class GraphQLResponse<Data: RootSelectionSet> {
public let body: JSONObject

private let rootKey: CacheReference
private let variables: [String: InputValue]?
private let variables: GraphQLOperation.Variables?

public init<Operation: GraphQLOperation>(operation: Operation, body: JSONObject) where Operation.Data == Data {
self.body = body
Expand Down
71 changes: 29 additions & 42 deletions Sources/Apollo/InputValue+Evaluation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,67 @@ import ApolloAPI
import Foundation

extension Selection.Field {
func cacheKey(with variables: [String: InputValue]?) throws -> String {
if
let argumentValues = try arguments?.evaluate(with: variables),
argumentValues.apollo.isNotEmpty {
let argumentsKey = orderIndependentKey(for: argumentValues)
return "\(name)(\(argumentsKey))"
func cacheKey(with variables: GraphQLOperation.Variables?) throws -> String {
if let arguments = arguments,
case let argumentValues = try InputValue.evaluate(arguments, with: variables),
argumentValues.apollo.isNotEmpty {
let argumentsKey = orderIndependentKey(for: argumentValues)
return "\(name)(\(argumentsKey))"
} else {
return name
}
}

private func orderIndependentKey(for object: JSONObject) -> String {
return object.sorted { $0.key < $1.key }.map {
if let object = $0.value as? JSONObject {
switch $0.value {
case let object as JSONObject:
return "[\($0.key):\(orderIndependentKey(for: object))]"
} else if let array = $0.value as? [JSONObject] {
case let array as [JSONObject]:
return "\($0.key):[\(array.map { orderIndependentKey(for: $0) }.joined(separator: ","))]"
} else {
case is NSNull:
return "\($0.key):null"
default:
return "\($0.key):\($0.value)"
}
}.joined(separator: ",")
}
}

extension Selection.Field.Arguments {

func evaluate(with variables: [String: InputValue]?) throws -> JSONObject {
/// `Selection.Field.Arguments` can only ever hold arguments of `.object` type. Which will
/// always resolve to an `.object` value. So force casting to `JSONObject` is safe.
return try arguments.evaluate(with: variables) as! JSONObject
}
}

extension InputValue {
func evaluate(with variables: [String: InputValue]?) throws -> JSONValue {
private func evaluate(with variables: GraphQLOperation.Variables?) throws -> JSONValue? {
switch self {
case let .scalar(value):
return value

case let .variable(name):
guard let value = variables?[name] else {
throw GraphQLError("Variable \"\(name)\" was not provided.")
}
return value.jsonEncodableValue?.jsonValue

switch value {
case let .variable(nestedName) where name == nestedName:
throw GraphQLError("Variable \"\(name)\" is infinitely recursive.")
default:
return try value.evaluate(with: variables)
}
case let .scalar(value):
return value

case let .list(array):
return try evaluate(values: array, with: variables)
return try InputValue.evaluate(array, with: variables)

case let .object(dictionary):
return try evaluate(values: dictionary, with: variables)
return try InputValue.evaluate(dictionary, with: variables)

case .none:
case .null:
return NSNull()
}
}

private func evaluate(values: [InputValue], with variables: [String: InputValue]?) throws -> [JSONValue] {
try values.map { try $0.evaluate(with: variables) }
fileprivate static func evaluate(
_ values: [InputValue],
with variables: GraphQLOperation.Variables?
) throws -> [JSONValue] {
try values.compactMap { try $0.evaluate(with: variables) }
}

private func evaluate(values: [String: InputValue], with variables: [String: InputValue]?) throws -> JSONObject {
var jsonObject = JSONObject(minimumCapacity: values.count)
for (key, value) in values {
let evaluatedValue = try value.evaluate(with: variables)
if !(evaluatedValue is NSNull) {
jsonObject[key] = evaluatedValue
}
}
return jsonObject
fileprivate static func evaluate(
_ values: [String: InputValue],
with variables: GraphQLOperation.Variables?
) throws -> JSONObject {
try values.compactMapValues { try $0.evaluate(with: variables) }
}
}
4 changes: 4 additions & 0 deletions Sources/Apollo/JSONSerializationFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public final class JSONSerializationFormat {
return try JSONSerialization.sortedData(withJSONObject: value.jsonValue)
}

public class func serialize(value: JSONObject) throws -> Data {
return try JSONSerialization.sortedData(withJSONObject: value)
}

public class func deserialize(data: Data) throws -> JSONValue {
return try JSONSerialization.jsonObject(with: data, options: [])
}
Expand Down
27 changes: 17 additions & 10 deletions Sources/Apollo/RequestBodyCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,32 @@ public protocol RequestBodyCreator {
/// - sendQueryDocument: Whether or not to send the full query document. Should default to `true`.
/// - autoPersistQuery: Whether to use auto-persisted query information. Should default to `false`.
/// - Returns: The created `GraphQLMap`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Returns: The created `GraphQLMap`
/// - Returns: The created `JSONEncodableDictionary`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

func requestBody<Operation: GraphQLOperation>(for operation: Operation,
sendOperationIdentifiers: Bool,
sendQueryDocument: Bool,
autoPersistQuery: Bool) -> GraphQLMap
func requestBody<Operation: GraphQLOperation>(
for operation: Operation,
sendOperationIdentifiers: Bool,
sendQueryDocument: Bool,
autoPersistQuery: Bool
) -> JSONEncodableDictionary
}

// MARK: - Default Implementation

extension RequestBodyCreator {

public func requestBody<Operation: GraphQLOperation>(for operation: Operation,
sendOperationIdentifiers: Bool,
sendQueryDocument: Bool,
autoPersistQuery: Bool) -> GraphQLMap {
var body: GraphQLMap = [
"variables": operation.variables?.jsonObject,
public func requestBody<Operation: GraphQLOperation>(
for operation: Operation,
sendOperationIdentifiers: Bool,
sendQueryDocument: Bool,
autoPersistQuery: Bool
) -> JSONEncodableDictionary {
var body: JSONEncodableDictionary = [
"operationName": operation.operationName,
]

if let variables = operation.variables {
body["variables"] = variables.jsonEncodableObject
}

if sendOperationIdentifiers {
guard let operationIdentifier = operation.operationIdentifier else {
preconditionFailure("To send operation identifiers, Apollo types must be generated with operationIdentifiers")
Expand Down
6 changes: 3 additions & 3 deletions Sources/Apollo/UploadRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ open class UploadRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
sendOperationIdentifiers: shouldSendOperationID,
sendQueryDocument: true,
autoPersistQuery: false)
var variables = fields["variables"] as? GraphQLMap ?? GraphQLMap()
var variables = fields["variables"] as? JSONEncodableDictionary ?? JSONEncodableDictionary()
for fieldName in fieldsForFiles {
if
let value = variables[fieldName],
let arrayValue = value as? [JSONEncodable] {
let arrayOfNils: [JSONEncodable?] = arrayValue.map { _ in nil }
let arrayOfNils: [JSONEncodable?] = arrayValue.map { _ in NSNull() }
variables.updateValue(arrayOfNils, forKey: fieldName)
} else {
variables.updateValue(nil, forKey: fieldName)
variables.updateValue(NSNull(), forKey: fieldName)
}
}
fields["variables"] = variables
Expand Down
Loading