You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
2.6 KiB
78 lines
2.6 KiB
5 months ago
|
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,
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
};
|
||
|
}
|