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

feat: Specify caching fields with typePolicy directive #554

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ input PetSearchFilters {
measurements: MeasurementsInput
}

interface Animal {
interface Animal @typePolicy(keyFields: "id") {
id: ID!
species: String!
height: Height!
predators: [Animal!]!
skinCovering: SkinCovering
}

interface Pet {
interface Pet @typePolicy(keyFields: "id") {
Copy link
Author

Choose a reason for hiding this comment

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

I added the directive to one of the schemas, but I didn't regenerate the example sources since that would add another 100 files to the Pull Request

Copy link
Contributor

Choose a reason for hiding this comment

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

That's fine while we do code review. But we should go ahead and generate those files and verify they look correct and compile prior to merging.

id: ID!
humanName: String
favoriteToy: String!
Expand Down Expand Up @@ -198,5 +198,5 @@ enum SkinCovering {
FUR
HAIR
FEATHERS
SCALES
SCALES
}
8 changes: 6 additions & 2 deletions Tests/ApolloCodegenInternalTestHelpers/MockGraphQLType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ public extension GraphQLObjectType {
_ name: String = "",
interfaces: [GraphQLInterfaceType] = [],
fields: [String: GraphQLField] = [:],
keyFields: [String] = [],
documentation: String? = nil
) -> GraphQLObjectType {
GraphQLObjectType(
name: GraphQLName(schemaName: name),
documentation: documentation,
fields: fields,
interfaces: interfaces
interfaces: interfaces,
keyFields: keyFields
)
}
}
Expand All @@ -35,14 +37,16 @@ public extension GraphQLInterfaceType {
class func mock(
_ name: String = "",
fields: [String: GraphQLField] = [:],
keyFields: [String] = [],
interfaces: [GraphQLInterfaceType] = [],
documentation: String? = nil
) -> GraphQLInterfaceType {
GraphQLInterfaceType(
name: GraphQLName(schemaName: name),
documentation: documentation,
fields: fields,
interfaces: interfaces
interfaces: interfaces,
keyFields: keyFields
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ final class AnimalKingdomIRCreationTests: XCTestCase {

func test__mergedSelections_AllAnimalsQuery_AllAnimal__isCorrect() async throws {
// given
let Interface_Animal = GraphQLInterfaceType.mock("Animal")
let Interface_Animal = GraphQLInterfaceType.mock("Animal", keyFields: ["id"])

try await buildOperation()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ class InterfaceTemplateTests: XCTestCase {
name: String = "Dog",
customName: String? = nil,
documentation: String? = nil,
keyFields: [String] = [],
config: ApolloCodegenConfiguration = .mock()
) {
let interfaceType = GraphQLInterfaceType.mock(
name,
fields: [:],
keyFields: keyFields,
interfaces: [],
documentation: documentation
)
Expand All @@ -46,7 +48,7 @@ class InterfaceTemplateTests: XCTestCase {
buildSubject(name: "aDog")

let expected = """
static let ADog = ApolloAPI.Interface(name: "aDog")
static let ADog = ApolloAPI.Interface(name: "aDog", keyFields: nil)
"""

// when
Expand All @@ -68,7 +70,7 @@ class InterfaceTemplateTests: XCTestCase {

let expected = """
/// \(documentation)
static let Dog = ApolloAPI.Interface(name: "Dog")
static let Dog = ApolloAPI.Interface(name: "Dog", keyFields: nil)
"""

// when
Expand All @@ -88,7 +90,7 @@ class InterfaceTemplateTests: XCTestCase {
)

let expected = """
static let Dog = ApolloAPI.Interface(name: "Dog")
static let Dog = ApolloAPI.Interface(name: "Dog", keyFields: nil)
"""

// when
Expand All @@ -108,7 +110,7 @@ class InterfaceTemplateTests: XCTestCase {
)

let expected = """
static let Dog = Apollo.Interface(name: "Dog")
static let Dog = Apollo.Interface(name: "Dog", keyFields: nil)
"""

// when
Expand All @@ -128,7 +130,7 @@ class InterfaceTemplateTests: XCTestCase {
buildSubject(name: keyword)

let expected = """
static let \(keyword.firstUppercased)_Interface = ApolloAPI.Interface(name: "\(keyword)")
static let \(keyword.firstUppercased)_Interface = ApolloAPI.Interface(name: "\(keyword)", keyFields: nil)
"""

// when
Expand All @@ -150,7 +152,28 @@ class InterfaceTemplateTests: XCTestCase {

let expected = """
// Renamed from GraphQL schema value: 'MyInterface'
static let MyCustomInterface = ApolloAPI.Interface(name: "MyInterface")
static let MyCustomInterface = ApolloAPI.Interface(name: "MyInterface", keyFields: nil)
"""

// when
let actual = renderSubject()

// then
expect(actual).to(equalLineByLine(expected))
}

func test__render__givenInterface_withKeyFields_shouldRenderKeyFields() throws {
// given
buildSubject(
name: "IndexedNode",
keyFields: ["parentID", "index"]
)

let expected = """
static let IndexedNode = ApolloAPI.Interface(name: "IndexedNode", keyFields: [
"parentID",
"index"
])
"""

// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ class ObjectTemplateTests: XCTestCase {
name: String = "Dog",
customName: String? = nil,
interfaces: [GraphQLInterfaceType] = [],
keyFields: [String] = [],
documentation: String? = nil,
config: ApolloCodegenConfiguration = .mock()
) {
let objectType = GraphQLObjectType.mock(
name,
interfaces: interfaces,
keyFields: keyFields,
documentation: documentation
)
objectType.name.customName = customName
Expand Down Expand Up @@ -82,7 +84,7 @@ class ObjectTemplateTests: XCTestCase {
implementedInterfaces: [
TestSchema.Interfaces.Animal.self,
TestSchema.Interfaces.Pet.self
]
],
"""

// when
Expand All @@ -106,7 +108,7 @@ class ObjectTemplateTests: XCTestCase {
implementedInterfaces: [
Interfaces.Animal.self,
Interfaces.Pet.self
]
],
"""

// when
Expand All @@ -121,7 +123,7 @@ class ObjectTemplateTests: XCTestCase {
buildSubject()

let expected = """
implementedInterfaces: []
implementedInterfaces: [],
"""

// when
Expand All @@ -130,6 +132,39 @@ class ObjectTemplateTests: XCTestCase {
// then
expect(actual).to(equalLineByLine(expected, atLine: 3, ignoringExtraLines: true))
}

func test_render_givenKeyField_rendersKeyFieldArray() {
// given
buildSubject(keyFields: ["id"])

let expected = """
keyFields: ["id"]
"""

// when
let actual = renderSubject()

// then
expect(actual).to(equalLineByLine(expected, atLine: 4, ignoringExtraLines: true))
}

func test_render_givenMultipleKeyFields_rendersKeyFieldArray() {
// given
buildSubject(keyFields: ["id", "species"])

let expected = """
keyFields: [
"id",
"species"
]
"""

// when
let actual = renderSubject()

// then
expect(actual).to(equalLineByLine(expected, atLine: 4, ignoringExtraLines: true))
}

// MARK: Documentation Tests

Expand Down Expand Up @@ -213,7 +248,8 @@ class ObjectTemplateTests: XCTestCase {
// Renamed from GraphQL schema value: 'MyObject'
static let MyCustomObject = ApolloAPI.Object(
typename: "MyObject",
implementedInterfaces: [TestSchema.Interfaces.MyCustomInterface.self]
implementedInterfaces: [TestSchema.Interfaces.MyCustomInterface.self],
keyFields: nil
)
"""

Expand Down
65 changes: 65 additions & 0 deletions Tests/ApolloTests/CacheKeyResolutionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,69 @@ class CacheKeyResolutionTests: XCTestCase {
expect(actual).to(equal("GreekLetters:δ"))
}

func test__schemaConfiguration__givenSingleKeyField_shouldReturnKeyFieldValue() {
let Delta = Object(typename: "Dog", implementedInterfaces: [], keyFields: ["id"])

MockSchemaMetadata.stub_objectTypeForTypeName({ _ in Delta })

let object: JSONObject = [
"__typename": "Dog",
"id": "10",
"name": "Beagle"
]

let objectDict = NetworkResponseExecutionSource().opaqueObjectDataWrapper(for: object)
let actual = MockSchemaMetadata.cacheKey(for: objectDict)

expect(actual).to(equal("Dog:10"))
}

func test__schemaConfiguration__givenMultipleKeyFields_shouldReturnKeyFieldValues() {
let Delta = Object(typename: "Dog", implementedInterfaces: [], keyFields: ["id", "name"])

MockSchemaMetadata.stub_objectTypeForTypeName({ _ in Delta })

let object: JSONObject = [
"__typename": "Dog",
"id": "10",
"name": #"Be\ag+le"#,
"height": 20,
]

let objectDict = NetworkResponseExecutionSource().opaqueObjectDataWrapper(for: object)
let actual = MockSchemaMetadata.cacheKey(for: objectDict)

expect(actual).to(equal(#"Dog:10+Be\\ag\+le"#))
}

func test__schemaConfiguration__givenMissingKeyFields_shouldReturnNil() {
let Delta = Object(typename: "Dog", implementedInterfaces: [], keyFields: ["id", "name"])

MockSchemaMetadata.stub_objectTypeForTypeName({ _ in Delta })

let object: JSONObject = [
"__typename": "Dog",
"id": "10",
]

let objectDict = NetworkResponseExecutionSource().opaqueObjectDataWrapper(for: object)
let actual = MockSchemaMetadata.cacheKey(for: objectDict)

expect(actual).to(beNil())
}

func test__schemaConfiguration__givenInterfaceWithKeyField_shouldReturnKeyFieldValue() {
let Interface = Interface(name: "Animal", keyFields: ["id"])

let object: JSONObject = [
"__typename": "Cat",
"id": "10",
]

let objectDict = NetworkResponseExecutionSource().opaqueObjectDataWrapper(for: object)
let actual = MockSchemaMetadata.cacheKey(for: objectDict, withInterface: Interface)

expect(actual).to(equal("Cat:10"))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ struct InterfaceTemplate: TemplateRenderer {
"""
\(documentation: graphqlInterface.documentation, config: config)
\(graphqlInterface.name.typeNameDocumentation)
static let \(graphqlInterface.render(as: .typename)) = \(config.ApolloAPITargetName).Interface(name: "\(graphqlInterface.name.schemaName)")
static let \(graphqlInterface.render(as: .typename)) = \(config.ApolloAPITargetName).Interface(name: "\(graphqlInterface.name.schemaName)", keyFields: \(KeyFieldsTemplate()))
"""
}

private func KeyFieldsTemplate() -> TemplateString {
guard let fields = graphqlInterface.keyFields, !fields.isEmpty else { return "nil" }

return """
[\(list: fields.map { "\"\($0)\"" })]
"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ struct ObjectTemplate: TemplateRenderer {
\(graphqlObject.name.typeNameDocumentation)
static let \(graphqlObject.render(as: .typename)) = \(config.ApolloAPITargetName).Object(
typename: "\(graphqlObject.name.schemaName)\",
implementedInterfaces: \(ImplementedInterfacesTemplate())
implementedInterfaces: \(ImplementedInterfacesTemplate()),
keyFields: \(KeyFieldsTemplate())
)
"""
}

private func KeyFieldsTemplate() -> TemplateString {
guard let fields = graphqlObject.keyFields, !fields.isEmpty else { return "nil" }

return """
[\(list: fields.map { "\"\($0)\"" })]
"""
}

private func ImplementedInterfacesTemplate() -> TemplateString {
return """
Expand Down

Large diffs are not rendered by default.

Loading