|
| 1 | +import Foundation |
| 2 | + |
1 | 3 | /// A protocol that a generated GraphQL schema should conform to.
|
2 | 4 | ///
|
3 | 5 | /// The generated schema metadata is the source of information about the generated types in the
|
@@ -43,20 +45,53 @@ extension SchemaMetadata {
|
43 | 45 | /// Resolves the cache key for an object in a GraphQL response to be used by
|
44 | 46 | /// `NormalizedCache` mechanisms.
|
45 | 47 | ///
|
46 |
| - /// Maps the type of the `object` using the ``graphQLType(for:)`` function, then gets the |
47 |
| - /// ``CacheKeyInfo`` for the `object` using the ``SchemaConfiguration/cacheKeyInfo(for:object:)`` |
48 |
| - /// function. |
49 |
| - /// Finally, this function transforms the ``CacheKeyInfo`` into the correct ``CacheReference`` |
50 |
| - /// for the `NormalizedCache`. |
| 48 | + /// The algorithm for resolving the objects cache key: |
| 49 | + /// 1. Map the type of the `object` using the ``graphQLType(for:)`` function. |
| 50 | + /// 2. Attempt to gets the `CacheKeyInfo`` using programmatic cache key configuration. |
| 51 | + /// 2a. Call the ``SchemaConfiguration/cacheKeyInfo(for:object:)`` function. |
| 52 | + /// 2b. If `CacheKeyInfo` is found, transforms the ``CacheKeyInfo`` into the correct ``CacheReference`` |
| 53 | + /// for the `NormalizedCache` and return it. |
| 54 | + /// 3. If no programmatic cache key is returned, attempt to resolve the `keyFields` for the object |
| 55 | + /// 3a. Check if the object's type has `keyFields`. |
| 56 | + /// 3b. If the type of the object is unknown (ie. it cannot be found by ``graphQLType(for:)``), |
| 57 | + /// or the type does not have `keyFields`, check if the inferred interface for the type has |
| 58 | + /// `keyFields`. |
| 59 | + /// 3c. If `keyFields` are found, resolve the cache key by escaping and joining the values of |
| 60 | + /// the `keyFields` on the object. Return the resolved cache key. |
| 61 | + /// 4. If a cache key is not resolved programmatically or using `keyFields`, return `nil`. |
51 | 62 | ///
|
52 | 63 | /// - Parameter object: A ``JSONObject`` dictionary representing an object in a GraphQL response.
|
| 64 | + /// - Parameter implementedInterface: An optional ``Interface`` that the object is |
| 65 | + /// inferred to implement. If the cache key is being resolved for a selection set with an |
| 66 | + /// interface as it's `__parentType`, you can infer the object must implement that interface. |
| 67 | + /// You should provide that interface to this parameter. |
| 68 | + /// |
53 | 69 | /// - Returns: A `String` representing the cache key for the `object` to be used by
|
54 | 70 | /// `NormalizedCache` mechanisms.
|
55 |
| - @inlinable public static func cacheKey(for object: ObjectData) -> String? { |
56 |
| - guard let type = graphQLType(for: object), |
57 |
| - let info = configuration.cacheKeyInfo(for: type, object: object) else { |
58 |
| - return nil |
| 71 | + @inlinable public static func cacheKey( |
| 72 | + for object: ObjectData, |
| 73 | + inferredToImplementInterface implementedInterface: Interface? = nil |
| 74 | + ) -> String? { |
| 75 | + guard let type = graphQLType(for: object) else { return nil } |
| 76 | + |
| 77 | + if let info = configuration.cacheKeyInfo(for: type, object: object) { |
| 78 | + return "\(info.uniqueKeyGroup ?? type.typename):\(info.id)" |
| 79 | + } |
| 80 | + |
| 81 | + guard let keyFields = type.keyFields ?? implementedInterface?.keyFields else { return nil } |
| 82 | + |
| 83 | + let idValues = try? keyFields.map { |
| 84 | + guard let keyFieldValue = object[$0] else { |
| 85 | + throw JSONDecodingError.missingValue |
| 86 | + } |
| 87 | + let item = try String(_jsonValue: keyFieldValue._asAnyHashable) |
| 88 | + |
| 89 | + // Escape all instances of `+` with a backslash, as well as other backslashes |
| 90 | + return item.replacingOccurrences(of: "\\", with: "\\\\") |
| 91 | + .replacingOccurrences(of: "+", with: "\\+") |
59 | 92 | }
|
60 |
| - return "\(info.uniqueKeyGroup ?? type.typename):\(info.id)" |
| 93 | + |
| 94 | + guard let id = idValues?.joined(separator: "+") else { return nil } |
| 95 | + return "\(type.typename):\(id)" |
61 | 96 | }
|
62 | 97 | }
|
0 commit comments