<NME> getFragmentIdentifier.js
<BEF> /**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 * @oncall relay
 */

'use strict';

import type {ReaderFragment} from './ReaderNode';

const {
  getDataIDsFromFragment,
  getSelector,
  getVariablesFromFragment,
} = require('../store/RelayModernSelector');
const isEmptyObject = require('./isEmptyObject');
const RelayFeatureFlags = require('./RelayFeatureFlags');
const stableCopy = require('./stableCopy');
const {intern} = require('./StringInterner');

function getFragmentIdentifier(
  fragmentNode: ReaderFragment,
  fragmentRef: mixed,
): string {
  const selector = getSelector(fragmentNode, fragmentRef);
  const fragmentOwnerIdentifier =
    selector == null
      ? 'null'
      : selector.kind === 'SingularReaderSelector'
      ? selector.owner.identifier
      : '[' +
        selector.selectors.map(sel => sel.owner.identifier).join(',') +
        ']';
  const fragmentVariables = getVariablesFromFragment(fragmentNode, fragmentRef);
  const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);

  );
  const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);

  // We don't need to include the fragment owner variables since those
  // are already encoded in the fragmentVariables
  const fragmentOwnerID = Array.isArray(fragmentOwner)
    ? fragmentOwner.map(
        owner => owner?.node.params.id ?? owner?.node.params.name ?? null,
      )
    : fragmentOwner != null
    ? fragmentOwner.node.params.id ?? fragmentOwner.node.params.name
    : null;

  return `${fragmentNode.name}-${JSON.stringify(
    stableCopy({
      dataIDs,
      fragmentVariables,
      fragmentOwnerID,
    }),
  )}`;
}
        : JSON.stringify(stableCopy(fragmentVariables))) +
      '/' +
      ids
    );
  } else {
    let ids = JSON.stringify(dataIDs) ?? 'missing';
    ids =
      RelayFeatureFlags.STRING_INTERN_LEVEL <= 1
        ? ids
        : intern(ids, RelayFeatureFlags.MAX_DATA_ID_LENGTH);

    return (
      fragmentOwnerIdentifier +
      '/' +
      fragmentNode.name +
      '/' +
      JSON.stringify(stableCopy(fragmentVariables)) +
      '/' +
      ids
    );
  }
}

module.exports = getFragmentIdentifier;

<MSG> Fix getFragmentIdentifier; include fragment owner vars

Reviewed By: alunyov

Differential Revision: D14681587

fbshipit-source-id: cfbb7ad723bfb9f9a00204660b21ef66a669bd87

<DFF> @@ -42,21 +42,21 @@ function getFragmentIdentifier(
   );
   const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);
 
-  // We don't need to include the fragment owner variables since those
-  // are already encoded in the fragmentVariables
   const fragmentOwnerID = Array.isArray(fragmentOwner)
     ? fragmentOwner.map(
-        owner => owner?.node.params.id ?? owner?.node.params.name ?? null,
+        owner => owner?.node.params.id ?? owner?.node.params.name ?? '',
       )
-    : fragmentOwner != null
-    ? fragmentOwner.node.params.id ?? fragmentOwner.node.params.name
-    : null;
+    : fragmentOwner?.node.params.id ?? fragmentOwner?.node.params.name ?? '';
+  const fragmentOwnerVariables = Array.isArray(fragmentOwner)
+    ? fragmentOwner.map(owner => owner?.variables ?? null)
+    : fragmentOwner?.variables ?? null;
 
   return `${fragmentNode.name}-${JSON.stringify(
     stableCopy({
       dataIDs,
       fragmentVariables,
       fragmentOwnerID,
+      fragmentOwnerVariables,
     }),
   )}`;
 }

{'commit': 'diff --git a/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js b/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js\nindex f5b55da69fe8..923a922789b9 100644\n--- a/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js\n+++ b/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js\n@@ -133,14 +133,14 @@ describe(\'getFragmentIdentifier\', () => {\n   it(\'returns correct identifier when fragment ref is null\', () => {\n     const identifier = getFragmentIdentifier(singularFragment, null);\n     expect(identifier).toEqual(\n-      \'UserFragment-{"dataIDs":null,"fragmentOwnerID":null,"fragmentVariables":{}}\',\n+      \'UserFragment-{"dataIDs":null,"fragmentOwnerID":"","fragmentOwnerVariables":null,"fragmentVariables":{}}\',\n     );\n   });\n \n   it(\'returns correct identifier when using plural fragment and fragment ref is empty\', () => {\n     const identifier = getFragmentIdentifier(pluralFragment, []);\n     expect(identifier).toEqual(\n-      \'UsersFragment-{"dataIDs":null,"fragmentOwnerID":[],"fragmentVariables":{}}\',\n+      \'UsersFragment-{"dataIDs":null,"fragmentOwnerID":[],"fragmentOwnerVariables":[],"fragmentVariables":{}}\',\n     );\n   });\n \n@@ -151,7 +151,7 @@ describe(\'getFragmentIdentifier\', () => {\n     ).data?.node;\n     const identifier = getFragmentIdentifier(singularFragment, fragmentRef);\n     expect(identifier).toEqual(\n-      \'UserFragment-{"dataIDs":"1","fragmentOwnerID":"UserQuery","fragmentVariables":{"scale":16}}\',\n+      \'UserFragment-{"dataIDs":"1","fragmentOwnerID":"UserQuery","fragmentOwnerVariables":{"id":"1","scale":16},"fragmentVariables":{"scale":16}}\',\n     );\n   });\n \n@@ -162,7 +162,7 @@ describe(\'getFragmentIdentifier\', () => {\n     ).data?.node;\n     const identifier = getFragmentIdentifier(fragmentWithArgs, fragmentRef);\n     expect(identifier).toEqual(\n-      \'UserFragmentWithArgs-{"dataIDs":"1","fragmentOwnerID":"UserQueryWithArgs","fragmentVariables":{"scaleLocal":16}}\',\n+      \'UserFragmentWithArgs-{"dataIDs":"1","fragmentOwnerID":"UserQueryWithArgs","fragmentOwnerVariables":{"id":"1","scale":16},"fragmentVariables":{"scaleLocal":16}}\',\n     );\n   });\n \n@@ -171,7 +171,7 @@ describe(\'getFragmentIdentifier\', () => {\n       .data?.nodes;\n     const identifier = getFragmentIdentifier(pluralFragment, fragmentRef);\n     expect(identifier).toEqual(\n-      \'UsersFragment-{"dataIDs":["1"],"fragmentOwnerID":["UsersQuery"],"fragmentVariables":{"scale":16}}\',\n+      \'UsersFragment-{"dataIDs":["1"],"fragmentOwnerID":["UsersQuery"],"fragmentOwnerVariables":[{"ids":["1"],"scale":16}],"fragmentVariables":{"scale":16}}\',\n     );\n   });\n });\ndiff --git a/packages/relay-runtime/util/getFragmentIdentifier.js b/packages/relay-runtime/util/getFragmentIdentifier.js\nindex bdbfbc701435..c0a4d4ef578f 100644\n--- a/packages/relay-runtime/util/getFragmentIdentifier.js\n+++ b/packages/relay-runtime/util/getFragmentIdentifier.js\n@@ -42,21 +42,21 @@ function getFragmentIdentifier(\n   );\n   const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);\n \n-  // We don\'t need to include the fragment owner variables since those\n-  // are already encoded in the fragmentVariables\n   const fragmentOwnerID = Array.isArray(fragmentOwner)\n     ? fragmentOwner.map(\n-        owner => owner?.node.params.id ?? owner?.node.params.name ?? null,\n+        owner => owner?.node.params.id ?? owner?.node.params.name ?? \'\',\n       )\n-    : fragmentOwner != null\n-    ? fragmentOwner.node.params.id ?? fragmentOwner.node.params.name\n-    : null;\n+    : fragmentOwner?.node.params.id ?? fragmentOwner?.node.params.name ?? \'\';\n+  const fragmentOwnerVariables = Array.isArray(fragmentOwner)\n+    ? fragmentOwner.map(owner => owner?.variables ?? null)\n+    : fragmentOwner?.variables ?? null;\n \n   return `${fragmentNode.name}-${JSON.stringify(\n     stableCopy({\n       dataIDs,\n       fragmentVariables,\n       fragmentOwnerID,\n+      fragmentOwnerVariables,\n     }),\n   )}`;\n }\n', 'commit_subject': 'Fix getFragmentIdentifier; include fragment owner vars', 'repo_name': 'facebook/relay', 'committer': {'name': 'Facebook Github Bot', 'email': 'f08905f3496a7b7d60e0da97307f6ad7594abc92@users.noreply.github.com', 'time_sec': '1554064539', 'tz_offset': '-420', 'date': {'seconds': '1554064539'}}, 'commit_tree': '3d42acbb1dbed6ad3e91a37eb455358b82489afa', 'commit_message': 'Fix getFragmentIdentifier; include fragment owner vars\n\nReviewed By: alunyov\n\nDifferential Revision: D14681587\n\nfbshipit-source-id: cfbb7ad723bfb9f9a00204660b21ef66a669bd87\n', 'language_name': 'TypeScript', 'license': 'mit', 'hunks': "@@ -42,21 +42,21 @@ function getFragmentIdentifier(\n   );\n   const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);\n \n-  // We don't need to include the fragment owner variables since those\n-  // are already encoded in the fragmentVariables\n   const fragmentOwnerID = Array.isArray(fragmentOwner)\n     ? fragmentOwner.map(\n-        owner => owner?.node.params.id ?? owner?.node.params.name ?? null,\n+        owner => owner?.node.params.id ?? owner?.node.params.name ?? '',\n       )\n-    : fragmentOwner != null\n-    ? fragmentOwner.node.params.id ?? fragmentOwner.node.params.name\n-    : null;\n+    : fragmentOwner?.node.params.id ?? fragmentOwner?.node.params.name ?? '';\n+  const fragmentOwnerVariables = Array.isArray(fragmentOwner)\n+    ? fragmentOwner.map(owner => owner?.variables ?? null)\n+    : fragmentOwner?.variables ?? null;\n \n   return `${fragmentNode.name}-${JSON.stringify(\n     stableCopy({\n       dataIDs,\n       fragmentVariables,\n       fragmentOwnerID,\n+      fragmentOwnerVariables,\n     }),\n   )}`;\n }\n", 'addition_count': 6, 'deletion_count': 6, 'src_file': 'a/packages/relay-runtime/util/getFragmentIdentifier.js', 'tgt_file': 'b/packages/relay-runtime/util/getFragmentIdentifier.js', 'file_extension': '.js', 'before_file': "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @flow strict-local\n * @format\n * @oncall relay\n */\n\n'use strict';\n\nimport type {ReaderFragment} from './ReaderNode';\n\nconst {\n  getDataIDsFromFragment,\n  getSelector,\n  getVariablesFromFragment,\n} = require('../store/RelayModernSelector');\nconst isEmptyObject = require('./isEmptyObject');\nconst RelayFeatureFlags = require('./RelayFeatureFlags');\nconst stableCopy = require('./stableCopy');\nconst {intern} = require('./StringInterner');\n\nfunction getFragmentIdentifier(\n  fragmentNode: ReaderFragment,\n  fragmentRef: mixed,\n): string {\n  const selector = getSelector(fragmentNode, fragmentRef);\n  const fragmentOwnerIdentifier =\n    selector == null\n      ? 'null'\n      : selector.kind === 'SingularReaderSelector'\n      ? selector.owner.identifier\n      : '[' +\n        selector.selectors.map(sel => sel.owner.identifier).join(',') +\n        ']';\n  const fragmentVariables = getVariablesFromFragment(fragmentNode, fragmentRef);\n  const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);\n\n  );\n  const dataIDs = getDataIDsFromFragment(fragmentNode, fragmentRef);\n\n  // We don't need to include the fragment owner variables since those\n  // are already encoded in the fragmentVariables\n  const fragmentOwnerID = Array.isArray(fragmentOwner)\n    ? fragmentOwner.map(\n        owner => owner?.node.params.id ?? owner?.node.params.name ?? null,\n      )\n    : fragmentOwner != null\n    ? fragmentOwner.node.params.id ?? fragmentOwner.node.params.name\n    : null;\n\n  return `${fragmentNode.name}-${JSON.stringify(\n    stableCopy({\n      dataIDs,\n      fragmentVariables,\n      fragmentOwnerID,\n    }),\n  )}`;\n}\n        : JSON.stringify(stableCopy(fragmentVariables))) +\n      '/' +\n      ids\n    );\n  } else {\n    let ids = JSON.stringify(dataIDs) ?? 'missing';\n    ids =\n      RelayFeatureFlags.STRING_INTERN_LEVEL <= 1\n        ? ids\n        : intern(ids, RelayFeatureFlags.MAX_DATA_ID_LENGTH);\n\n    return (\n      fragmentOwnerIdentifier +\n      '/' +\n      fragmentNode.name +\n      '/' +\n      JSON.stringify(stableCopy(fragmentVariables)) +\n      '/' +\n      ids\n    );\n  }\n}\n\nmodule.exports = getFragmentIdentifier;\n", 'message': 'Fix getFragmentIdentifier; include fragment owner vars\n\nReviewed By: alunyov\n\nDifferential Revision: D14681587\n\nfbshipit-source-id: cfbb7ad723bfb9f9a00204660b21ef66a669bd87\n', 'author_name': 'Facebook Github Bot', 'lang': 'js'}