Skip to content

Commit eb3a8cb

Browse files
gh-action-runnergh-action-runner
authored andcommitted
Squashed 'apollo-ios-codegen/' changes from 1b1661ba..b214a4ac
b214a4ac fix: Generate local cache mutation referenced fragments as mutable (#659) git-subtree-dir: apollo-ios-codegen git-subtree-split: b214a4ac2bd541274428068cf0b58219bc753f1d
1 parent f9dc31a commit eb3a8cb

File tree

6 files changed

+171
-19
lines changed

6 files changed

+171
-19
lines changed

Sources/GraphQLCompiler/ApolloCodegenFrontendBundle.swift

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

Sources/GraphQLCompiler/CompilationResult.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,15 @@ public final class CompilationResult: JavaScriptObjectDecodable {
230230
public let filePath: String
231231

232232
public var isLocalCacheMutation: Bool {
233-
directives?.contains { $0.name == Constants.DirectiveNames.LocalCacheMutation } ?? false
233+
overrideAsLocalCacheMutation || directives?.contains {
234+
$0.name == Constants.DirectiveNames.LocalCacheMutation
235+
} ?? false
234236
}
235237

236238
public let moduleImports: OrderedSet<String>
237239

240+
public let overrideAsLocalCacheMutation: Bool
241+
238242
init(_ jsValue: JSValue, bridge: isolated JavaScriptBridge) {
239243
self.name = jsValue["name"]
240244
self.directives = .fromJSValue(jsValue["directives"], bridge: bridge)
@@ -243,8 +247,11 @@ public final class CompilationResult: JavaScriptObjectDecodable {
243247
self.referencedFragments = .fromJSValue(jsValue["referencedFragments"], bridge: bridge)
244248
self.source = jsValue["source"]
245249
self.filePath = jsValue["filePath"]
246-
self.moduleImports = FragmentDefinition.getImportModuleNames(directives: directives,
247-
referencedFragments: referencedFragments)
250+
self.moduleImports = FragmentDefinition.getImportModuleNames(
251+
directives: directives,
252+
referencedFragments: referencedFragments
253+
)
254+
self.overrideAsLocalCacheMutation = jsValue["overrideAsLocalCacheMutation"]
248255
}
249256

250257
/// Initializer to be used for creating mock objects in tests only.
@@ -255,7 +262,8 @@ public final class CompilationResult: JavaScriptObjectDecodable {
255262
directives: [Directive]?,
256263
referencedFragments: [FragmentDefinition],
257264
source: String,
258-
filePath: String
265+
filePath: String,
266+
overrideAsLocalCacheMutation: Bool
259267
) {
260268
self.name = name
261269
self.type = type
@@ -264,8 +272,11 @@ public final class CompilationResult: JavaScriptObjectDecodable {
264272
self.referencedFragments = referencedFragments
265273
self.source = source
266274
self.filePath = filePath
267-
self.moduleImports = FragmentDefinition.getImportModuleNames(directives: directives,
268-
referencedFragments: referencedFragments)
275+
self.moduleImports = FragmentDefinition.getImportModuleNames(
276+
directives: directives,
277+
referencedFragments: referencedFragments
278+
)
279+
self.overrideAsLocalCacheMutation = overrideAsLocalCacheMutation
269280
}
270281

271282
public var debugDescription: String {

Sources/GraphQLCompiler/JavaScript/src/__tests__/referencedFragmentsTests.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,114 @@ describe("operation with referencedFragments on child entity selection sets", ()
184184
});
185185

186186
});
187+
188+
describe("local cache mutation operation with referenced fragments", () => {
189+
const schemaSDL: string =
190+
`type Query {
191+
allAnimals: [Animal!]
192+
}
193+
194+
interface Animal {
195+
species: String!
196+
friend: Animal!
197+
}`;
198+
199+
const schema: GraphQLSchema = loadSchemaFromSources([new Source(schemaSDL, "Test Schema", { line: 1, column: 1 })]);
200+
201+
const documentString: string =
202+
`query Test @apollo_client_ios_localCacheMutation {
203+
allAnimals {
204+
...SpeciesFragment
205+
}
206+
}
207+
208+
fragment SpeciesFragment on Animal {
209+
species
210+
...FriendFragment
211+
}
212+
213+
fragment FriendFragment on Animal {
214+
friend {
215+
species
216+
}
217+
}`;
218+
219+
const document: DocumentNode = parseOperationDocument(
220+
new Source(documentString, "Test Query", { line: 1, column: 1 })
221+
);
222+
223+
it("should flag the referenced fragments as being local cache mutations too.", () => {
224+
const compilationResult: CompilationResult = compileDocument(schema, document, false, false, emptyValidationOptions);
225+
226+
const speciesFragment: FragmentDefinition = compilationResult.fragments.find(function (element) {
227+
return element.name == 'SpeciesFragment'
228+
}) as FragmentDefinition
229+
const friendFragment: FragmentDefinition = compilationResult.fragments.find(function (element) {
230+
return element.name == 'FriendFragment'
231+
}) as FragmentDefinition
232+
233+
expect(speciesFragment.overrideAsLocalCacheMutation).toBeTruthy();
234+
expect(friendFragment.overrideAsLocalCacheMutation).toBeTruthy();
235+
});
236+
});
237+
238+
describe("local cache mutation fragment with referenced fragments", () => {
239+
const schemaSDL: string =
240+
`type Query {
241+
allAnimals: [Animal!]
242+
}
243+
244+
interface Animal {
245+
name: String!
246+
species: String!
247+
friend: Animal!
248+
}`;
249+
250+
const schema: GraphQLSchema = loadSchemaFromSources([new Source(schemaSDL, "Test Schema", { line: 1, column: 1 })]);
251+
252+
const documentString: string =
253+
`query Test {
254+
allAnimals {
255+
...NameFragment
256+
...SpeciesFragment
257+
}
258+
}
259+
260+
fragment NameFragment on Animal {
261+
name
262+
}
263+
264+
fragment SpeciesFragment on Animal @apollo_client_ios_localCacheMutation {
265+
species
266+
...FriendFragment
267+
}
268+
269+
fragment FriendFragment on Animal {
270+
friend {
271+
species
272+
}
273+
}`;
274+
275+
const document: DocumentNode = parseOperationDocument(
276+
new Source(documentString, "Test Query", { line: 1, column: 1 })
277+
);
278+
279+
it("should only flag the cache mutation referenced fragment as being local cache mutation.", () => {
280+
const compilationResult: CompilationResult = compileDocument(schema, document, false, false, emptyValidationOptions);
281+
282+
const nameFragment: FragmentDefinition = compilationResult.fragments.find(function (element) {
283+
return element.name == 'NameFragment'
284+
}) as FragmentDefinition
285+
const speciesFragment: FragmentDefinition = compilationResult.fragments.find(function (element) {
286+
return element.name == 'SpeciesFragment'
287+
}) as FragmentDefinition
288+
const friendFragment: FragmentDefinition = compilationResult.fragments.find(function (element) {
289+
return element.name == 'FriendFragment'
290+
}) as FragmentDefinition
291+
292+
expect(nameFragment.overrideAsLocalCacheMutation).toBeFalsy();
293+
expect(speciesFragment.overrideAsLocalCacheMutation).toBeFalsy();
294+
295+
expect(friendFragment.overrideAsLocalCacheMutation).toBeTruthy();
296+
});
297+
});

Sources/GraphQLCompiler/JavaScript/src/compiler/index.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
containsLocalCacheMutationDirective,
23
getFieldDef,
34
isMetaFieldName,
45
isNotNullOrUndefined,
@@ -229,18 +230,21 @@ export function compileToIR(
229230

230231
addReferencedType(rootType)
231232

233+
const selectionSet = compileSelectionSet(operationDefinition.selectionSet, rootType, referencedFragments)
234+
const referencedFragmentsArray = Array.from(referencedFragments.values())
235+
236+
if (containsLocalCacheMutationDirective(operationDefinition.directives)) {
237+
overrideAsLocalCacheMutation(referencedFragmentsArray);
238+
}
239+
232240
return {
233241
name,
234242
operationType,
235243
variables,
236244
rootType,
237-
selectionSet: compileSelectionSet(
238-
operationDefinition.selectionSet,
239-
rootType,
240-
referencedFragments
241-
),
245+
selectionSet: selectionSet,
242246
directives: directives,
243-
referencedFragments: Array.from(referencedFragments.values()),
247+
referencedFragments: referencedFragmentsArray,
244248
source,
245249
filePath
246250
};
@@ -267,21 +271,34 @@ export function compileToIR(
267271

268272
addReferencedType(getNamedType(typeCondition));
269273

274+
const selectionSet = compileSelectionSet(fragmentDefinition.selectionSet, typeCondition, referencedFragments)
275+
const referencedFragmentsArray = Array.from(referencedFragments.values())
276+
277+
if (containsLocalCacheMutationDirective(fragmentDefinition.directives)) {
278+
overrideAsLocalCacheMutation(referencedFragmentsArray);
279+
}
280+
270281
return {
271282
name,
272283
filePath,
273284
source,
274285
typeCondition,
275-
selectionSet: compileSelectionSet(
276-
fragmentDefinition.selectionSet,
277-
typeCondition,
278-
referencedFragments
279-
),
286+
selectionSet: selectionSet,
280287
directives: directives,
281-
referencedFragments: Array.from(referencedFragments.values()),
288+
referencedFragments: referencedFragmentsArray,
289+
overrideAsLocalCacheMutation: false
282290
};
283291
}
284292

293+
function overrideAsLocalCacheMutation(
294+
fragments: ir.FragmentDefinition[]
295+
) {
296+
fragments.forEach(element => {
297+
element.overrideAsLocalCacheMutation = true
298+
overrideAsLocalCacheMutation(element.referencedFragments)
299+
});
300+
}
301+
285302
function compileSelectionSet(
286303
selectionSetNode: SelectionSetNode,
287304
parentType: GraphQLCompositeType,

Sources/GraphQLCompiler/JavaScript/src/compiler/ir.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export interface FragmentDefinition {
4242
referencedFragments: FragmentDefinition[];
4343
source: string;
4444
filePath?: string;
45+
overrideAsLocalCacheMutation: boolean;
4546
}
4647

4748
export interface SelectionSet {

Sources/GraphQLCompiler/JavaScript/src/utilities/graphql.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ export function isMetaFieldName(name: string) {
5959
return name.startsWith("__");
6060
}
6161

62+
export function containsLocalCacheMutationDirective(directives: readonly DirectiveNode[] | undefined): boolean {
63+
if (!directives) return false
64+
65+
for (const directive of directives) {
66+
if (directive.name.value == directive_apollo_client_ios_localCacheMutation.name.value) {
67+
return true
68+
}
69+
}
70+
71+
return false
72+
}
73+
6274
const typenameField: FieldNode = {
6375
kind: Kind.FIELD,
6476
name: { kind: Kind.NAME, value: "__typename" },

0 commit comments

Comments
 (0)