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

feature: Generated models support the @defer directive #31

Merged
merged 23 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0a4e0db
Added supporting changes for deferred fragments
calvincestari Sep 5, 2023
3b6543a
Add tests to validate deferred inline fragment
calvincestari Sep 11, 2023
f14ee8b
Add tests to validate deferred named fragment
calvincestari Sep 11, 2023
123d98a
Another test
calvincestari Sep 12, 2023
83dba17
Add nested deferred inline fragment test
calvincestari Sep 12, 2023
0cc1391
Label is no longer required for named fragments
calvincestari Sep 14, 2023
14ba7d3
Added documentation
calvincestari Sep 14, 2023
d4afea7
Update Tests/ApolloCodegenTests/CodeGeneration/Templates/SelectionSet…
calvincestari Sep 14, 2023
ec4643a
Update Tests/ApolloCodegenTests/CodeGeneration/Templates/SelectionSet…
calvincestari Sep 14, 2023
0e7c48b
Fixed tests
calvincestari Sep 14, 2023
1df8903
Remove isDeferred from ScopeCondition
calvincestari Sep 20, 2023
57f5b0b
Add deferred collection to ScopeDescriptor
calvincestari Sep 20, 2023
42d5191
Fix test mocks
calvincestari Sep 20, 2023
ff9d579
Update operation template for deferred property
calvincestari Sep 20, 2023
1adb93d
Refactor for deferred named fragment spreads
calvincestari Sep 20, 2023
9b1808d
Combine deferred properties of IR built named fragments into operation
calvincestari Sep 20, 2023
9e7416e
Refactor scope descriptor evaluation
calvincestari Sep 22, 2023
7bc5f32
Add inline documentation
calvincestari Sep 22, 2023
d8c4ad8
Remove selection set template defer tests
calvincestari Sep 22, 2023
14559fd
Updated JS frontend bundle
calvincestari Sep 22, 2023
2faef51
Update graphql-js parsing response matchers
calvincestari Sep 22, 2023
df3e425
Add RootFieldBuilder tests
calvincestari Sep 22, 2023
eb9ffea
Fix compilation result for string arguments
calvincestari Sep 22, 2023
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
6 changes: 4 additions & 2 deletions Tests/ApolloCodegenInternalTestHelpers/IR+Mocking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ extension IR.Operation {

public static func mock(
definition: CompilationResult.OperationDefinition? = nil,
referencedFragments: OrderedSet<IR.NamedFragment> = []
referencedFragments: OrderedSet<IR.NamedFragment> = [],
hasDeferredFragments: Bool = false
) -> IR.Operation {
let definition = definition ?? .mock()
return IR.Operation.init(
Expand All @@ -114,7 +115,8 @@ extension IR.Operation {
givenAllTypesInSchema: .init([]))
])
),
referencedFragments: referencedFragments
referencedFragments: referencedFragments,
hasDeferredFragments: hasDeferredFragments
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,129 @@ class OperationDefinitionTemplateTests: XCTestCase {
expect(actual).to(equalLineByLine(expected, ignoringExtraLines: true))
}

// MARK: Selection Set Declaration
// MARK: - Defer Properties

func test__generate__givenQueryWithDeferredInlineFragment_generatesDeferredPropertyTrue() throws {
// given
schemaSDL = """
type Query {
allAnimals: [Animal!]
}

interface Animal {
species: String!
}

type Dog implements Animal {
species: String!
}
"""

document = """
query TestOperation {
allAnimals {
... on Dog @defer(label: "root") {
species
}
}
}
"""

let expected = """
public static let hasDeferredFragments: Bool = true
"""

// when
try buildSubjectAndOperation()
let actual = renderSubject()

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

func test__generate__givenQueryWithDeferredNamedFragment_generatesDeferredPropertyTrue() throws {
// given
schemaSDL = """
type Query {
allAnimals: [Animal!]
}

interface Animal {
species: String!
}

type Dog implements Animal {
species: String!
}
"""

document = """
query TestOperation {
allAnimals {
... DogFragment @defer(label: "root")
}
}

fragment DogFragment on Dog {
species
}
"""

let expected = """
public static let hasDeferredFragments: Bool = true
"""

// when
try buildSubjectAndOperation()
let actual = renderSubject()

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

func test__generate__givenQueryWithNamedFragment_withDeferredTypeCase_generatesDeferredPropertyTrue() throws {
// given
schemaSDL = """
type Query {
allAnimals: [Animal!]
}

interface Animal {
species: String!
}

type Dog implements Animal {
species: String!
}
"""

document = """
query TestOperation {
allAnimals {
... DogFragment
}
}

fragment DogFragment on Animal {
... on Dog @defer(label: "root") {
species
}
}
"""

let expected = """
public static let hasDeferredFragments: Bool = true
"""

// when
try buildSubjectAndOperation()
let actual = renderSubject()

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

// MARK: - Selection Set Declaration

func test__generate__givenOperationSelectionSet_rendersDeclaration() throws {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7071,4 +7071,4 @@ class SelectionSetTemplateTests: XCTestCase {
expect(actual).to(equalLineByLine(expected, atLine: 6, ignoringExtraLines: true))
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ struct OperationDefinitionTemplate: OperationTemplateRenderer {
accessControlRenderer: { accessControlModifier(for: .member) }()
))

\(section: DeferredProperties(operation.hasDeferredFragments))

\(section: VariableProperties(operation.definition.variables))

\(Initializer(operation.definition.variables))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,14 @@ extension OperationTemplateRenderer {
"""
}

func DeferredProperties(
_ hasDeferredFragments: Bool
) -> TemplateString {
return """
\(if: hasDeferredFragments, """
public static let hasDeferredFragments: Bool = true
""")
"""
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public class CompilationResult: JavaScriptObject {

lazy var directives: [Directive]? = self["directives"]

lazy var isDeferred: IsDeferred = getIsDeferred()
public lazy var isDeferred: IsDeferred = getIsDeferred()

public override var debugDescription: String {
selectionSet.debugDescription
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ describe("operation with referencedFragments", () => {
...FragmentA
...FragmentC
}
`, "OperationA", { line: 1, column: 1 }),
false
`, "OperationA", { line: 1, column: 1 })
Copy link
Member Author

Choose a reason for hiding this comment

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

This is due to the move from graphql-js 16 to 17.

);

const fragmentADocument: DocumentNode = parseOperationDocument(
Expand All @@ -35,26 +34,23 @@ describe("operation with referencedFragments", () => {
a
...FragmentB
}
`, "FragmentA", { line: 1, column: 1 }),
false
`, "FragmentA", { line: 1, column: 1 })
);

const fragmentBDocument: DocumentNode = parseOperationDocument(
new Source( `
fragment FragmentB on Query {
b
}
`, "FragmentB", { line: 1, column: 1 }),
false
`, "FragmentB", { line: 1, column: 1 })
);

const fragmentCDocument: DocumentNode = parseOperationDocument(
new Source( `
fragment FragmentC on Query {
c
}
`, "FragmentC", { line: 1, column: 1 }),
false
`, "FragmentC", { line: 1, column: 1 })
);

const schema: GraphQLSchema = loadSchemaFromSources([new Source(`
Expand Down Expand Up @@ -109,8 +105,7 @@ describe("operation with referencedFragments on child entity selection sets", ()
}
...FragmentC
}
`, "OperationA", { line: 1, column: 1 }),
false
`, "OperationA", { line: 1, column: 1 })
);

const fragmentADocument: DocumentNode = parseOperationDocument(
Expand All @@ -121,26 +116,23 @@ describe("operation with referencedFragments on child entity selection sets", ()
...FragmentB
}
}
`, "FragmentA", { line: 1, column: 1 }),
false
`, "FragmentA", { line: 1, column: 1 })
);

const fragmentBDocument: DocumentNode = parseOperationDocument(
new Source( `
fragment FragmentB on B {
B
}
`, "FragmentB", { line: 1, column: 1 }),
false
`, "FragmentB", { line: 1, column: 1 })
);

const fragmentCDocument: DocumentNode = parseOperationDocument(
new Source( `
fragment FragmentC on Query {
c
}
`, "FragmentC", { line: 1, column: 1 }),
false
`, "FragmentC", { line: 1, column: 1 })
);

const schema: GraphQLSchema = loadSchemaFromSources([new Source(`
Expand Down Expand Up @@ -191,4 +183,4 @@ describe("operation with referencedFragments on child entity selection sets", ()
expect(fragmentA.referencedFragments).toEqual([fragmentB])
});

});
});
11 changes: 4 additions & 7 deletions apollo-ios-codegen/Sources/IR/IR+EntitySelectionTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,7 @@ class EntitySelectionTree {
fileprivate func scopeConditionNode(for condition: ScopeCondition) -> EntityNode {
let nodeCondition = ScopeCondition(
type: condition.type == self.type ? nil : condition.type,
conditions: condition.conditions,
isDeferred: condition.isDeferred
conditions: condition.conditions
)

func createNode() -> EntityNode {
Expand Down Expand Up @@ -408,8 +407,7 @@ extension EntitySelectionTree.EntityNode {
for conditionGroup in inclusionConditions.elements {
let scope = ScopeCondition(
type: rootTypesMatch ? nil : fragmentType,
conditions: conditionGroup,
isDeferred: fragment.isDeferred
conditions: conditionGroup
)
let nextNode = rootNodeToStartMerge.scopeConditionNode(for: scope)

Expand All @@ -424,7 +422,7 @@ extension EntitySelectionTree.EntityNode {
let nextNode = rootTypesMatch ?
rootNodeToStartMerge :
rootNodeToStartMerge.scopeConditionNode(
for: ScopeCondition(type: fragmentType, isDeferred: fragment.isDeferred)
for: ScopeCondition(type: fragmentType)
)

nextNode.mergeIn(
Expand Down Expand Up @@ -516,8 +514,7 @@ extension EntitySelectionTree.EntityNode {
entity: entity,
scopePath: oldFragment.typeInfo.scopePath
),
inclusionConditions: oldFragment.inclusionConditions,
isDeferred: oldFragment.isDeferred
inclusionConditions: oldFragment.inclusionConditions
)
}

Expand Down
14 changes: 3 additions & 11 deletions apollo-ios-codegen/Sources/IR/IR+InlineFragmentSpread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,30 @@ public class InlineFragmentSpread: Hashable, CustomDebugStringConvertible {
/// enclosing operation/fragment.
public let selectionSet: SelectionSet

public let isDeferred: IsDeferred

/// Indicates the location where the inline fragment has been "spread into" its enclosing
/// operation/fragment.
public var typeInfo: SelectionSet.TypeInfo { selectionSet.typeInfo }

public var inclusionConditions: InclusionConditions? { selectionSet.inclusionConditions }

init(
selectionSet: SelectionSet,
isDeferred: IsDeferred
) {
init(selectionSet: SelectionSet) {
self.selectionSet = selectionSet
self.isDeferred = isDeferred
}

public static func == (lhs: InlineFragmentSpread, rhs: InlineFragmentSpread) -> Bool {
lhs.selectionSet == rhs.selectionSet &&
lhs.isDeferred == rhs.isDeferred
lhs.selectionSet == rhs.selectionSet
}

public func hash(into hasher: inout Hasher) {
hasher.combine(selectionSet)
hasher.combine(isDeferred)
}

public var debugDescription: String {
var string = typeInfo.parentType.debugDescription
if let conditions = typeInfo.inclusionConditions {
string += " \(conditions.debugDescription)"
}
string += isDeferred.definitionDirectiveDescription

return string
}
}
12 changes: 12 additions & 0 deletions apollo-ios-codegen/Sources/IR/IR+IsDeferred.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import GraphQLCompiler

// TODO: Documentation for this to be completed in issue #3141
public enum IsDeferred: Hashable, ExpressibleByBooleanLiteral {
case value(Bool)
Expand All @@ -12,6 +14,16 @@ public enum IsDeferred: Hashable, ExpressibleByBooleanLiteral {
}
}

init(_ compilationResult: CompilationResult.IsDeferred) {
switch compilationResult {
case let .value(value):
self = .value(value)

case let .variable(variable):
self = .if(variable)
}
}

var definitionDirectiveDescription: String {
switch self {
case .value(false): return ""
Expand Down
Loading