Skip to content

Commit ac3a252

Browse files
rhagigifacebook-github-bot
authored andcommittedAug 5, 2020
Optimize getFragmentIdentifier
Reviewed By: josephsavona Differential Revision: D21529406 fbshipit-source-id: 4975af012219d0f8effe8c39a9ce3397d14133ed
1 parent 1b91824 commit ac3a252

File tree

3 files changed

+222
-9
lines changed

3 files changed

+222
-9
lines changed
 

‎packages/relay-runtime/util/RelayFeatureFlags.js

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type FeatureFlags = {|
1919
ENABLE_PRECISE_TYPE_REFINEMENT: boolean,
2020
ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
2121
ENABLE_REQUIRED_DIRECTIVES: boolean | 'LIMITED',
22+
ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
2223
|};
2324

2425
const RelayFeatureFlags: FeatureFlags = {
@@ -28,6 +29,7 @@ const RelayFeatureFlags: FeatureFlags = {
2829
ENABLE_PRECISE_TYPE_REFINEMENT: false,
2930
ENABLE_REACT_FLIGHT_COMPONENT_FIELD: false,
3031
ENABLE_REQUIRED_DIRECTIVES: false,
32+
ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: false,
3133
};
3234

3335
module.exports = RelayFeatureFlags;

‎packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js

+180
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// flowlint ambiguous-object-type:error
1313

1414
'use strict';
15+
const RelayFeatureFlags = require('../RelayFeatureFlags');
1516

1617
const getFragmentIdentifier = require('../getFragmentIdentifier');
1718
const invariant = require('invariant');
@@ -40,6 +41,7 @@ describe('getFragmentIdentifier', () => {
4041
let pluralVariables;
4142

4243
beforeEach(() => {
44+
RelayFeatureFlags.ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION = false;
4345
environment = createMockEnvironment();
4446
const generated = generateAndCompile(`
4547
fragment NestedUserFragment on User {
@@ -191,3 +193,181 @@ describe('getFragmentIdentifier', () => {
191193
);
192194
});
193195
});
196+
197+
describe('getFragmentIdentifier Optimized', () => {
198+
let environment;
199+
let gqlSingularQuery;
200+
let gqlSingularFragment;
201+
let gqlPluralQuery;
202+
let gqlPluralFragment;
203+
let gqlQueryWithArgs;
204+
let gqlFragmentWithArgs;
205+
let singularFragment;
206+
let singularVariables;
207+
let singularQuery;
208+
let pluralQuery;
209+
let pluralFragment;
210+
let queryWithArgs;
211+
let fragmentWithArgs;
212+
let pluralVariables;
213+
214+
beforeEach(() => {
215+
RelayFeatureFlags.ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION = true;
216+
environment = createMockEnvironment();
217+
const generated = generateAndCompile(`
218+
fragment NestedUserFragment on User {
219+
username
220+
}
221+
222+
fragment UserFragment on User {
223+
id
224+
name
225+
profile_picture(scale: $scale) {
226+
uri
227+
}
228+
...NestedUserFragment
229+
}
230+
231+
fragment UserFragmentWithArgs on User
232+
@argumentDefinitions(scaleLocal: {type: "Float!"}) {
233+
id
234+
name
235+
profile_picture(scale: $scaleLocal) {
236+
uri
237+
}
238+
...NestedUserFragment
239+
}
240+
241+
fragment UsersFragment on User @relay(plural: true) {
242+
id
243+
name
244+
profile_picture(scale: $scale) {
245+
uri
246+
}
247+
...NestedUserFragment
248+
}
249+
250+
query UsersQuery($ids: [ID!]!, $scale: Float!) {
251+
nodes(ids: $ids) {
252+
...UsersFragment
253+
}
254+
}
255+
256+
query UserQuery($id: ID!, $scale: Float!) {
257+
node(id: $id) {
258+
...UserFragment
259+
}
260+
}
261+
262+
query UserQueryWithArgs($id: ID!, $scale: Float!) {
263+
node(id: $id) {
264+
...UserFragmentWithArgs @arguments(scaleLocal: $scale)
265+
}
266+
}
267+
`);
268+
pluralVariables = {ids: ['1'], scale: 16};
269+
singularVariables = {id: '1', scale: 16};
270+
gqlQueryWithArgs = generated.UserQueryWithArgs;
271+
gqlFragmentWithArgs = generated.UserFragmentWithArgs;
272+
gqlPluralQuery = generated.UsersQuery;
273+
gqlPluralFragment = generated.UsersFragment;
274+
gqlSingularQuery = generated.UserQuery;
275+
gqlSingularFragment = generated.UserFragment;
276+
pluralQuery = createOperationDescriptor(gqlPluralQuery, pluralVariables);
277+
singularQuery = singularQuery = createOperationDescriptor(
278+
gqlSingularQuery,
279+
singularVariables,
280+
);
281+
queryWithArgs = createOperationDescriptor(
282+
gqlQueryWithArgs,
283+
singularVariables,
284+
);
285+
singularFragment = getFragment(gqlSingularFragment);
286+
pluralFragment = getFragment(gqlPluralFragment);
287+
fragmentWithArgs = getFragment(gqlFragmentWithArgs);
288+
environment.commitPayload(singularQuery, {
289+
node: {
290+
__typename: 'User',
291+
id: '1',
292+
name: 'Alice',
293+
username: 'useralice',
294+
profile_picture: null,
295+
},
296+
});
297+
environment.commitPayload(pluralQuery, {
298+
nodes: [
299+
{
300+
__typename: 'User',
301+
id: '1',
302+
name: 'Alice',
303+
username: 'useralice',
304+
profile_picture: null,
305+
},
306+
{
307+
__typename: 'User',
308+
id: '2',
309+
name: 'Bob',
310+
username: 'userbob',
311+
profile_picture: null,
312+
},
313+
],
314+
});
315+
});
316+
317+
afterEach(() => {
318+
RelayFeatureFlags.ENABLE_VARIABLE_CONNECTION_KEY = false;
319+
});
320+
321+
it('returns correct identifier when fragment ref is undefined', () => {
322+
const identifier = getFragmentIdentifier(singularFragment, undefined);
323+
expect(identifier).toEqual('null/UserFragment/{}/missing');
324+
});
325+
326+
it('returns correct identifier when fragment ref is null', () => {
327+
const identifier = getFragmentIdentifier(singularFragment, null);
328+
expect(identifier).toEqual('null/UserFragment/{}/null');
329+
});
330+
331+
it('returns correct identifier when using plural fragment and fragment ref is empty', () => {
332+
const identifier = getFragmentIdentifier(pluralFragment, []);
333+
expect(identifier).toEqual('null/UsersFragment/{}/null');
334+
});
335+
336+
it('returns correct identifier when using singular fragment', () => {
337+
const fragmentRef = environment.lookup(singularQuery.fragment).data?.node;
338+
const identifier = getFragmentIdentifier(singularFragment, fragmentRef);
339+
expect(identifier).toEqual(
340+
singularQuery.request.identifier + '/UserFragment/{"scale":16}/1',
341+
);
342+
});
343+
344+
it('returns correct identifier when using fragment with variables', () => {
345+
const fragmentRef = environment.lookup(queryWithArgs.fragment).data?.node;
346+
const identifier = getFragmentIdentifier(fragmentWithArgs, fragmentRef);
347+
expect(identifier).toEqual(
348+
queryWithArgs.request.identifier +
349+
'/UserFragmentWithArgs/{"scaleLocal":16}/1',
350+
);
351+
});
352+
353+
it('returns correct identifier when using plural fragment with single element', () => {
354+
const fragmentRef = environment.lookup(pluralQuery.fragment).data?.nodes;
355+
invariant(Array.isArray(fragmentRef), 'Expected a plural fragment ref.');
356+
const identifier = getFragmentIdentifier(pluralFragment, [fragmentRef[0]]);
357+
expect(identifier).toEqual(
358+
'[' + pluralQuery.request.identifier + ']/UsersFragment/{"scale":16}/[1]',
359+
);
360+
});
361+
362+
it('returns correct identifier when using plural fragment', () => {
363+
const fragmentRef = environment.lookup(pluralQuery.fragment).data?.nodes;
364+
const identifier = getFragmentIdentifier(pluralFragment, fragmentRef);
365+
expect(identifier).toEqual(
366+
'[' +
367+
pluralQuery.request.identifier +
368+
',' +
369+
pluralQuery.request.identifier +
370+
']/UsersFragment/{"scale":16}/[1,2]',
371+
);
372+
});
373+
});

‎packages/relay-runtime/util/getFragmentIdentifier.js

+40-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
'use strict';
1515

16+
const RelayFeatureFlags = require('./RelayFeatureFlags');
17+
1618
const stableCopy = require('./stableCopy');
1719

1820
const {
@@ -21,6 +23,7 @@ const {
2123
getSelector,
2224
} = require('../store/RelayModernSelector');
2325

26+
import type {Variables} from '../util/RelayRuntimeTypes';
2427
import type {ReaderFragment} from './ReaderNode';
2528

2629
function getFragmentIdentifier(
@@ -38,15 +41,43 @@ function getFragmentIdentifier(
3841
']';
3942
const fragmentVariables = getVariablesFromFragment(fragmentNode, fragmentRef);
4043
const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);
41-
return (
42-
fragmentOwnerIdentifier +
43-
'/' +
44-
fragmentNode.name +
45-
'/' +
46-
JSON.stringify(stableCopy(fragmentVariables)) +
47-
'/' +
48-
(JSON.stringify(dataIDs) ?? 'missing')
49-
);
44+
45+
if (RelayFeatureFlags.ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION) {
46+
return (
47+
fragmentOwnerIdentifier +
48+
'/' +
49+
fragmentNode.name +
50+
'/' +
51+
(fragmentVariables == null || isEmpty(fragmentVariables)
52+
? '{}'
53+
: JSON.stringify(stableCopy(fragmentVariables))) +
54+
'/' +
55+
(typeof dataIDs === 'undefined'
56+
? 'missing'
57+
: dataIDs == null
58+
? 'null'
59+
: Array.isArray(dataIDs)
60+
? '[' + dataIDs.join(',') + ']'
61+
: dataIDs)
62+
);
63+
} else {
64+
return (
65+
fragmentOwnerIdentifier +
66+
'/' +
67+
fragmentNode.name +
68+
'/' +
69+
JSON.stringify(stableCopy(fragmentVariables)) +
70+
'/' +
71+
(JSON.stringify(dataIDs) ?? 'missing')
72+
);
73+
}
74+
}
75+
76+
function isEmpty(obj: Variables): boolean {
77+
for (var x in obj) {
78+
return false;
79+
}
80+
return true;
5081
}
5182

5283
module.exports = getFragmentIdentifier;

0 commit comments

Comments
 (0)
Please sign in to comment.