<NME> typename-on-query.invalid.graphql
<BEF> ADDFILE
<MSG> Disallow typename on Root

Reviewed By: josephsavona

Differential Revision: D19953903

fbshipit-source-id: 8863cc4d5215af0016438dd09ddf34e4710eb47b

<DFF> @@ -0,0 +1,7 @@
+# expected-to-throw
+query TestQuery {
+  __typename
+  me {
+    name
+  }
+}

{'commit': "diff --git a/packages/relay-compiler/core/RelayIRTransforms.js b/packages/relay-compiler/core/RelayIRTransforms.js\nindex bc768d35a43d..4585e4c89f45 100644\n--- a/packages/relay-compiler/core/RelayIRTransforms.js\n+++ b/packages/relay-compiler/core/RelayIRTransforms.js\n@@ -17,6 +17,7 @@ const ClientExtensionsTransform = require('../transforms/ClientExtensionsTransfo\n const ConnectionTransform = require('../transforms/ConnectionTransform');\n const DeferStreamTransform = require('../transforms/DeferStreamTransform');\n const DisallowIdAsAlias = require('../transforms/DisallowIdAsAlias');\n+const DisallowTypenameOnRoot = require('../transforms/DisallowTypenameOnRoot');\n const FieldHandleTransform = require('../transforms/FieldHandleTransform');\n const FilterDirectivesTransform = require('../transforms/FilterDirectivesTransform');\n const FlattenTransform = require('../transforms/FlattenTransform');\n@@ -79,6 +80,7 @@ const relayFragmentTransforms: $ReadOnlyArray<IRTransform> = [\n // Transforms applied to queries/mutations/subscriptions that are used for\n // fetching data from the server and parsing those responses.\n const relayQueryTransforms: $ReadOnlyArray<IRTransform> = [\n+  DisallowTypenameOnRoot.transform,\n   ValidateUnusedVariablesTransform.transform,\n   ApplyFragmentArgumentTransform.transform,\n   ValidateGlobalVariablesTransform.transform,\ndiff --git a/packages/relay-compiler/transforms/DisallowTypenameOnRoot.js b/packages/relay-compiler/transforms/DisallowTypenameOnRoot.js\nnew file mode 100644\nindex 000000000000..04541aeda7aa\n--- /dev/null\n+++ b/packages/relay-compiler/transforms/DisallowTypenameOnRoot.js\n@@ -0,0 +1,45 @@\n+/**\n+ * Copyright (c) Facebook, Inc. and its 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+ */\n+\n+// flowlint ambiguous-object-type:error\n+\n+'use strict';\n+\n+const IRValidator = require('../core/IRValidator');\n+\n+const {createUserError} = require('../core/CompilerError');\n+\n+import type CompilerContext from '../core/CompilerContext';\n+import type {Root} from '../core/IR';\n+\n+function visitRoot(node: Root) {\n+  for (const selection of node.selections) {\n+    if (selection.kind === 'ScalarField' && selection.name === '__typename') {\n+      throw createUserError(\n+        'Relay does not allow `__typename` field on Query, Mutation or Subscription',\n+        [selection.loc],\n+      );\n+    }\n+  }\n+}\n+\n+function stopVisit() {}\n+\n+function disallowTypenameOnRoot(context: CompilerContext): CompilerContext {\n+  IRValidator.validate(context, {\n+    Root: visitRoot,\n+    Fragment: stopVisit,\n+  });\n+  return context;\n+}\n+\n+module.exports = {\n+  transform: disallowTypenameOnRoot,\n+};\ndiff --git a/packages/relay-compiler/transforms/__tests__/DisallowTypeNameOnRoot-test.js b/packages/relay-compiler/transforms/__tests__/DisallowTypeNameOnRoot-test.js\nnew file mode 100644\nindex 000000000000..8ef5c5b8adc1\n--- /dev/null\n+++ b/packages/relay-compiler/transforms/__tests__/DisallowTypeNameOnRoot-test.js\n@@ -0,0 +1,37 @@\n+/**\n+ * Copyright (c) Facebook, Inc. and its 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+ * @emails oncall+relay\n+ */\n+\n+// flowlint ambiguous-object-type:error\n+\n+'use strict';\n+\n+const CompilerContext = require('../../core/CompilerContext');\n+const DisallowTypenameOnRoot = require('../DisallowTypenameOnRoot');\n+const IRPrinter = require('../../core/IRPrinter');\n+\n+const {\n+  TestSchema,\n+  generateTestsFromFixtures,\n+  parseGraphQLText,\n+} = require('relay-test-utils-internal');\n+\n+generateTestsFromFixtures(\n+  `${__dirname}/fixtures/disallow-typename-on-root`,\n+  text => {\n+    const {definitions} = parseGraphQLText(TestSchema, text);\n+    return new CompilerContext(TestSchema)\n+      .addAll(definitions)\n+      .applyTransforms([DisallowTypenameOnRoot.transform])\n+      .documents()\n+      .map(doc => IRPrinter.print(TestSchema, doc))\n+      .join('\\n');\n+  },\n+);\ndiff --git a/packages/relay-compiler/transforms/__tests__/__snapshots__/DisallowTypeNameOnRoot-test.js.snap b/packages/relay-compiler/transforms/__tests__/__snapshots__/DisallowTypeNameOnRoot-test.js.snap\nnew file mode 100644\nindex 000000000000..abd1ec297a27\n--- /dev/null\n+++ b/packages/relay-compiler/transforms/__tests__/__snapshots__/DisallowTypeNameOnRoot-test.js.snap\n@@ -0,0 +1,44 @@\n+// Jest Snapshot v1, https://goo.gl/fbAQLP\n+\n+exports[`matches expected output: typename-on-mutation.invalid.graphql 1`] = `\n+~~~~~~~~~~ INPUT ~~~~~~~~~~\n+# expected-to-throw\n+mutation TestMutation {\n+  __typename\n+}\n+\n+~~~~~~~~~~ OUTPUT ~~~~~~~~~~\n+THROWN EXCEPTION:\n+\n+Relay does not allow \\`__typename\\` field on Query, Mutation or Subscription\n+\n+Source: GraphQL request (3:3)\n+2: mutation TestMutation {\n+3:   __typename\n+     ^\n+4: }\n+\n+`;\n+\n+exports[`matches expected output: typename-on-query.invalid.graphql 1`] = `\n+~~~~~~~~~~ INPUT ~~~~~~~~~~\n+# expected-to-throw\n+query TestQuery {\n+  __typename\n+  me {\n+    name\n+  }\n+}\n+\n+~~~~~~~~~~ OUTPUT ~~~~~~~~~~\n+THROWN EXCEPTION:\n+\n+Relay does not allow \\`__typename\\` field on Query, Mutation or Subscription\n+\n+Source: GraphQL request (3:3)\n+2: query TestQuery {\n+3:   __typename\n+     ^\n+4:   me {\n+\n+`;\ndiff --git a/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-mutation.invalid.graphql b/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-mutation.invalid.graphql\nnew file mode 100644\nindex 000000000000..f5cb01553ff4\n--- /dev/null\n+++ b/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-mutation.invalid.graphql\n@@ -0,0 +1,4 @@\n+# expected-to-throw\n+mutation TestMutation {\n+  __typename\n+}\ndiff --git a/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-query.invalid.graphql b/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-query.invalid.graphql\nnew file mode 100644\nindex 000000000000..856da95aaa18\n--- /dev/null\n+++ b/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-query.invalid.graphql\n@@ -0,0 +1,7 @@\n+# expected-to-throw\n+query TestQuery {\n+  __typename\n+  me {\n+    name\n+  }\n+}\n", 'commit_subject': 'Disallow typename on Root', 'repo_name': 'facebook/relay', 'committer': {'name': 'Facebook Github Bot', 'email': 'f08905f3496a7b7d60e0da97307f6ad7594abc92@users.noreply.github.com', 'time_sec': '1582058802', 'tz_offset': '-480', 'date': {'seconds': '1582058802'}}, 'commit_tree': '2266804945aa568cddb34a1d84ee7401512d724e', 'commit_message': 'Disallow typename on Root\n\nReviewed By: josephsavona\n\nDifferential Revision: D19953903\n\nfbshipit-source-id: 8863cc4d5215af0016438dd09ddf34e4710eb47b\n', 'language_name': 'TypeScript', 'license': 'mit', 'hunks': '@@ -0,0 +1,7 @@\n+# expected-to-throw\n+query TestQuery {\n+  __typename\n+  me {\n+    name\n+  }\n+}\n', 'addition_count': 7, 'deletion_count': 0, 'src_file': '/dev/null', 'tgt_file': 'b/packages/relay-compiler/transforms/__tests__/fixtures/disallow-typename-on-root/typename-on-query.invalid.graphql', 'file_extension': '.graphql', 'before_file': 'ADDFILE', 'message': 'Disallow typename on Root\n\nReviewed By: josephsavona\n\nDifferential Revision: D19953903\n\nfbshipit-source-id: 8863cc4d5215af0016438dd09ddf34e4710eb47b\n', 'author_name': 'Facebook Github Bot', 'lang': 'invalid'}