| import { GraphQLError } from '../../error/GraphQLError.mjs'; |
| import { Kind } from '../../language/kinds.mjs'; |
| import { collectFields } from '../../execution/collectFields.mjs'; |
|
|
| /** |
| * Subscriptions must only include a non-introspection field. |
| * |
| * A GraphQL subscription is valid only if it contains a single root field and |
| * that root field is not an introspection field. |
| * |
| * See https://spec.graphql.org/draft/#sec-Single-root-field |
| */ |
| export function SingleFieldSubscriptionsRule(context) { |
| return { |
| OperationDefinition(node) { |
| if (node.operation === 'subscription') { |
| const schema = context.getSchema(); |
| const subscriptionType = schema.getSubscriptionType(); |
|
|
| if (subscriptionType) { |
| const operationName = node.name ? node.name.value : null; |
| const variableValues = Object.create(null); |
| const document = context.getDocument(); |
| const fragments = Object.create(null); |
|
|
| for (const definition of document.definitions) { |
| if (definition.kind === Kind.FRAGMENT_DEFINITION) { |
| fragments[definition.name.value] = definition; |
| } |
| } |
|
|
| const fields = collectFields( |
| schema, |
| fragments, |
| variableValues, |
| subscriptionType, |
| node.selectionSet, |
| ); |
|
|
| if (fields.size > 1) { |
| const fieldSelectionLists = [...fields.values()]; |
| const extraFieldSelectionLists = fieldSelectionLists.slice(1); |
| const extraFieldSelections = extraFieldSelectionLists.flat(); |
| context.reportError( |
| new GraphQLError( |
| operationName != null |
| ? `Subscription "${operationName}" must select only one top level field.` |
| : 'Anonymous Subscription must select only one top level field.', |
| { |
| nodes: extraFieldSelections, |
| }, |
| ), |
| ); |
| } |
|
|
| for (const fieldNodes of fields.values()) { |
| const field = fieldNodes[0]; |
| const fieldName = field.name.value; |
|
|
| if (fieldName.startsWith('__')) { |
| context.reportError( |
| new GraphQLError( |
| operationName != null |
| ? `Subscription "${operationName}" must not select an introspection top level field.` |
| : 'Anonymous Subscription must not select an introspection top level field.', |
| { |
| nodes: fieldNodes, |
| }, |
| ), |
| ); |
| } |
| } |
| } |
| } |
| }, |
| }; |
| } |