Initial commit
This commit is contained in:
commit
f47e08fdcb
2409 files changed
+363607
No files matched your search
+272
@@ -0,0 +1,272 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ValidationContext = exports.SDLValidationContext = exports.ASTValidationContext = void 0;
|
||||
|
||||
var _visitor = require("../language/visitor");
|
||||
|
||||
var _kinds = require("../language/kinds");
|
||||
|
||||
var _TypeInfo = require("../utilities/TypeInfo");
|
||||
|
||||
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
|
||||
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
/**
|
||||
* An instance of this class is passed as the "this" context to all validators,
|
||||
* allowing access to commonly useful contextual information from within a
|
||||
* validation rule.
|
||||
*/
|
||||
var ASTValidationContext =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function ASTValidationContext(ast) {
|
||||
_defineProperty(this, "_ast", void 0);
|
||||
|
||||
_defineProperty(this, "_errors", void 0);
|
||||
|
||||
this._ast = ast;
|
||||
this._errors = [];
|
||||
}
|
||||
|
||||
var _proto = ASTValidationContext.prototype;
|
||||
|
||||
_proto.reportError = function reportError(error) {
|
||||
this._errors.push(error);
|
||||
};
|
||||
|
||||
_proto.getErrors = function getErrors() {
|
||||
return this._errors;
|
||||
};
|
||||
|
||||
_proto.getDocument = function getDocument() {
|
||||
return this._ast;
|
||||
};
|
||||
|
||||
return ASTValidationContext;
|
||||
}();
|
||||
|
||||
exports.ASTValidationContext = ASTValidationContext;
|
||||
|
||||
var SDLValidationContext =
|
||||
/*#__PURE__*/
|
||||
function (_ASTValidationContext) {
|
||||
_inheritsLoose(SDLValidationContext, _ASTValidationContext);
|
||||
|
||||
function SDLValidationContext(ast, schema) {
|
||||
var _this;
|
||||
|
||||
_this = _ASTValidationContext.call(this, ast) || this;
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "_schema", void 0);
|
||||
|
||||
_this._schema = schema;
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto2 = SDLValidationContext.prototype;
|
||||
|
||||
_proto2.getSchema = function getSchema() {
|
||||
return this._schema;
|
||||
};
|
||||
|
||||
return SDLValidationContext;
|
||||
}(ASTValidationContext);
|
||||
|
||||
exports.SDLValidationContext = SDLValidationContext;
|
||||
|
||||
var ValidationContext =
|
||||
/*#__PURE__*/
|
||||
function (_ASTValidationContext2) {
|
||||
_inheritsLoose(ValidationContext, _ASTValidationContext2);
|
||||
|
||||
function ValidationContext(schema, ast, typeInfo) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _ASTValidationContext2.call(this, ast) || this;
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_schema", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_typeInfo", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_fragments", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_fragmentSpreads", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_recursivelyReferencedFragments", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_variableUsages", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_recursiveVariableUsages", void 0);
|
||||
|
||||
_this2._schema = schema;
|
||||
_this2._typeInfo = typeInfo;
|
||||
_this2._fragmentSpreads = new Map();
|
||||
_this2._recursivelyReferencedFragments = new Map();
|
||||
_this2._variableUsages = new Map();
|
||||
_this2._recursiveVariableUsages = new Map();
|
||||
return _this2;
|
||||
}
|
||||
|
||||
var _proto3 = ValidationContext.prototype;
|
||||
|
||||
_proto3.getSchema = function getSchema() {
|
||||
return this._schema;
|
||||
};
|
||||
|
||||
_proto3.getFragment = function getFragment(name) {
|
||||
var fragments = this._fragments;
|
||||
|
||||
if (!fragments) {
|
||||
this._fragments = fragments = this.getDocument().definitions.reduce(function (frags, statement) {
|
||||
if (statement.kind === _kinds.Kind.FRAGMENT_DEFINITION) {
|
||||
frags[statement.name.value] = statement;
|
||||
}
|
||||
|
||||
return frags;
|
||||
}, Object.create(null));
|
||||
}
|
||||
|
||||
return fragments[name];
|
||||
};
|
||||
|
||||
_proto3.getFragmentSpreads = function getFragmentSpreads(node) {
|
||||
var spreads = this._fragmentSpreads.get(node);
|
||||
|
||||
if (!spreads) {
|
||||
spreads = [];
|
||||
var setsToVisit = [node];
|
||||
|
||||
while (setsToVisit.length !== 0) {
|
||||
var set = setsToVisit.pop();
|
||||
|
||||
for (var i = 0; i < set.selections.length; i++) {
|
||||
var selection = set.selections[i];
|
||||
|
||||
if (selection.kind === _kinds.Kind.FRAGMENT_SPREAD) {
|
||||
spreads.push(selection);
|
||||
} else if (selection.selectionSet) {
|
||||
setsToVisit.push(selection.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._fragmentSpreads.set(node, spreads);
|
||||
}
|
||||
|
||||
return spreads;
|
||||
};
|
||||
|
||||
_proto3.getRecursivelyReferencedFragments = function getRecursivelyReferencedFragments(operation) {
|
||||
var fragments = this._recursivelyReferencedFragments.get(operation);
|
||||
|
||||
if (!fragments) {
|
||||
fragments = [];
|
||||
var collectedNames = Object.create(null);
|
||||
var nodesToVisit = [operation.selectionSet];
|
||||
|
||||
while (nodesToVisit.length !== 0) {
|
||||
var node = nodesToVisit.pop();
|
||||
var spreads = this.getFragmentSpreads(node);
|
||||
|
||||
for (var i = 0; i < spreads.length; i++) {
|
||||
var fragName = spreads[i].name.value;
|
||||
|
||||
if (collectedNames[fragName] !== true) {
|
||||
collectedNames[fragName] = true;
|
||||
var fragment = this.getFragment(fragName);
|
||||
|
||||
if (fragment) {
|
||||
fragments.push(fragment);
|
||||
nodesToVisit.push(fragment.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._recursivelyReferencedFragments.set(operation, fragments);
|
||||
}
|
||||
|
||||
return fragments;
|
||||
};
|
||||
|
||||
_proto3.getVariableUsages = function getVariableUsages(node) {
|
||||
var usages = this._variableUsages.get(node);
|
||||
|
||||
if (!usages) {
|
||||
var newUsages = [];
|
||||
var typeInfo = new _TypeInfo.TypeInfo(this._schema);
|
||||
(0, _visitor.visit)(node, (0, _visitor.visitWithTypeInfo)(typeInfo, {
|
||||
VariableDefinition: function VariableDefinition() {
|
||||
return false;
|
||||
},
|
||||
Variable: function Variable(variable) {
|
||||
newUsages.push({
|
||||
node: variable,
|
||||
type: typeInfo.getInputType(),
|
||||
defaultValue: typeInfo.getDefaultValue()
|
||||
});
|
||||
}
|
||||
}));
|
||||
usages = newUsages;
|
||||
|
||||
this._variableUsages.set(node, usages);
|
||||
}
|
||||
|
||||
return usages;
|
||||
};
|
||||
|
||||
_proto3.getRecursiveVariableUsages = function getRecursiveVariableUsages(operation) {
|
||||
var usages = this._recursiveVariableUsages.get(operation);
|
||||
|
||||
if (!usages) {
|
||||
usages = this.getVariableUsages(operation);
|
||||
var fragments = this.getRecursivelyReferencedFragments(operation);
|
||||
|
||||
for (var i = 0; i < fragments.length; i++) {
|
||||
Array.prototype.push.apply(usages, this.getVariableUsages(fragments[i]));
|
||||
}
|
||||
|
||||
this._recursiveVariableUsages.set(operation, usages);
|
||||
}
|
||||
|
||||
return usages;
|
||||
};
|
||||
|
||||
_proto3.getType = function getType() {
|
||||
return this._typeInfo.getType();
|
||||
};
|
||||
|
||||
_proto3.getParentType = function getParentType() {
|
||||
return this._typeInfo.getParentType();
|
||||
};
|
||||
|
||||
_proto3.getInputType = function getInputType() {
|
||||
return this._typeInfo.getInputType();
|
||||
};
|
||||
|
||||
_proto3.getParentInputType = function getParentInputType() {
|
||||
return this._typeInfo.getParentInputType();
|
||||
};
|
||||
|
||||
_proto3.getFieldDef = function getFieldDef() {
|
||||
return this._typeInfo.getFieldDef();
|
||||
};
|
||||
|
||||
_proto3.getDirective = function getDirective() {
|
||||
return this._typeInfo.getDirective();
|
||||
};
|
||||
|
||||
_proto3.getArgument = function getArgument() {
|
||||
return this._typeInfo.getArgument();
|
||||
};
|
||||
|
||||
return ValidationContext;
|
||||
}(ASTValidationContext);
|
||||
|
||||
exports.ValidationContext = ValidationContext;
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ObjMap } from '../jsutils/ObjMap';
|
||||
import type { GraphQLError } from '../error';
|
||||
import { visit, visitWithTypeInfo } from '../language/visitor';
|
||||
import { Kind } from '../language/kinds';
|
||||
import type {
|
||||
DocumentNode,
|
||||
OperationDefinitionNode,
|
||||
VariableNode,
|
||||
SelectionSetNode,
|
||||
FragmentSpreadNode,
|
||||
FragmentDefinitionNode,
|
||||
} from '../language/ast';
|
||||
import type { ASTVisitor } from '../language/visitor';
|
||||
import type { GraphQLSchema } from '../type/schema';
|
||||
import type {
|
||||
GraphQLInputType,
|
||||
GraphQLOutputType,
|
||||
GraphQLCompositeType,
|
||||
GraphQLField,
|
||||
GraphQLArgument,
|
||||
} from '../type/definition';
|
||||
import type { GraphQLDirective } from '../type/directives';
|
||||
import { TypeInfo } from '../utilities/TypeInfo';
|
||||
|
||||
type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode;
|
||||
type VariableUsage = {|
|
||||
+node: VariableNode,
|
||||
+type: ?GraphQLInputType,
|
||||
+defaultValue: ?mixed,
|
||||
|};
|
||||
|
||||
/**
|
||||
* An instance of this class is passed as the "this" context to all validators,
|
||||
* allowing access to commonly useful contextual information from within a
|
||||
* validation rule.
|
||||
*/
|
||||
export class ASTValidationContext {
|
||||
_ast: DocumentNode;
|
||||
_errors: Array<GraphQLError>;
|
||||
|
||||
constructor(ast: DocumentNode): void {
|
||||
this._ast = ast;
|
||||
this._errors = [];
|
||||
}
|
||||
|
||||
reportError(error: GraphQLError): void {
|
||||
this._errors.push(error);
|
||||
}
|
||||
|
||||
getErrors(): $ReadOnlyArray<GraphQLError> {
|
||||
return this._errors;
|
||||
}
|
||||
|
||||
getDocument(): DocumentNode {
|
||||
return this._ast;
|
||||
}
|
||||
}
|
||||
|
||||
export class SDLValidationContext extends ASTValidationContext {
|
||||
_schema: ?GraphQLSchema;
|
||||
|
||||
constructor(ast: DocumentNode, schema?: ?GraphQLSchema): void {
|
||||
super(ast);
|
||||
this._schema = schema;
|
||||
}
|
||||
|
||||
getSchema(): ?GraphQLSchema {
|
||||
return this._schema;
|
||||
}
|
||||
}
|
||||
|
||||
export type SDLValidationRule = SDLValidationContext => ASTVisitor;
|
||||
|
||||
export class ValidationContext extends ASTValidationContext {
|
||||
_schema: GraphQLSchema;
|
||||
_typeInfo: TypeInfo;
|
||||
_fragments: ObjMap<FragmentDefinitionNode>;
|
||||
_fragmentSpreads: Map<SelectionSetNode, $ReadOnlyArray<FragmentSpreadNode>>;
|
||||
_recursivelyReferencedFragments: Map<
|
||||
OperationDefinitionNode,
|
||||
$ReadOnlyArray<FragmentDefinitionNode>,
|
||||
>;
|
||||
_variableUsages: Map<NodeWithSelectionSet, $ReadOnlyArray<VariableUsage>>;
|
||||
_recursiveVariableUsages: Map<
|
||||
OperationDefinitionNode,
|
||||
$ReadOnlyArray<VariableUsage>,
|
||||
>;
|
||||
|
||||
constructor(
|
||||
schema: GraphQLSchema,
|
||||
ast: DocumentNode,
|
||||
typeInfo: TypeInfo,
|
||||
): void {
|
||||
super(ast);
|
||||
this._schema = schema;
|
||||
this._typeInfo = typeInfo;
|
||||
this._fragmentSpreads = new Map();
|
||||
this._recursivelyReferencedFragments = new Map();
|
||||
this._variableUsages = new Map();
|
||||
this._recursiveVariableUsages = new Map();
|
||||
}
|
||||
|
||||
getSchema(): GraphQLSchema {
|
||||
return this._schema;
|
||||
}
|
||||
|
||||
getFragment(name: string): ?FragmentDefinitionNode {
|
||||
let fragments = this._fragments;
|
||||
if (!fragments) {
|
||||
this._fragments = fragments = this.getDocument().definitions.reduce(
|
||||
(frags, statement) => {
|
||||
if (statement.kind === Kind.FRAGMENT_DEFINITION) {
|
||||
frags[statement.name.value] = statement;
|
||||
}
|
||||
return frags;
|
||||
},
|
||||
Object.create(null),
|
||||
);
|
||||
}
|
||||
return fragments[name];
|
||||
}
|
||||
|
||||
getFragmentSpreads(
|
||||
node: SelectionSetNode,
|
||||
): $ReadOnlyArray<FragmentSpreadNode> {
|
||||
let spreads = this._fragmentSpreads.get(node);
|
||||
if (!spreads) {
|
||||
spreads = [];
|
||||
const setsToVisit: Array<SelectionSetNode> = [node];
|
||||
while (setsToVisit.length !== 0) {
|
||||
const set = setsToVisit.pop();
|
||||
for (let i = 0; i < set.selections.length; i++) {
|
||||
const selection = set.selections[i];
|
||||
if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
||||
spreads.push(selection);
|
||||
} else if (selection.selectionSet) {
|
||||
setsToVisit.push(selection.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._fragmentSpreads.set(node, spreads);
|
||||
}
|
||||
return spreads;
|
||||
}
|
||||
|
||||
getRecursivelyReferencedFragments(
|
||||
operation: OperationDefinitionNode,
|
||||
): $ReadOnlyArray<FragmentDefinitionNode> {
|
||||
let fragments = this._recursivelyReferencedFragments.get(operation);
|
||||
if (!fragments) {
|
||||
fragments = [];
|
||||
const collectedNames = Object.create(null);
|
||||
const nodesToVisit: Array<SelectionSetNode> = [operation.selectionSet];
|
||||
while (nodesToVisit.length !== 0) {
|
||||
const node = nodesToVisit.pop();
|
||||
const spreads = this.getFragmentSpreads(node);
|
||||
for (let i = 0; i < spreads.length; i++) {
|
||||
const fragName = spreads[i].name.value;
|
||||
if (collectedNames[fragName] !== true) {
|
||||
collectedNames[fragName] = true;
|
||||
const fragment = this.getFragment(fragName);
|
||||
if (fragment) {
|
||||
fragments.push(fragment);
|
||||
nodesToVisit.push(fragment.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._recursivelyReferencedFragments.set(operation, fragments);
|
||||
}
|
||||
return fragments;
|
||||
}
|
||||
|
||||
getVariableUsages(node: NodeWithSelectionSet): $ReadOnlyArray<VariableUsage> {
|
||||
let usages = this._variableUsages.get(node);
|
||||
if (!usages) {
|
||||
const newUsages = [];
|
||||
const typeInfo = new TypeInfo(this._schema);
|
||||
visit(
|
||||
node,
|
||||
visitWithTypeInfo(typeInfo, {
|
||||
VariableDefinition: () => false,
|
||||
Variable(variable) {
|
||||
newUsages.push({
|
||||
node: variable,
|
||||
type: typeInfo.getInputType(),
|
||||
defaultValue: typeInfo.getDefaultValue(),
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
usages = newUsages;
|
||||
this._variableUsages.set(node, usages);
|
||||
}
|
||||
return usages;
|
||||
}
|
||||
|
||||
getRecursiveVariableUsages(
|
||||
operation: OperationDefinitionNode,
|
||||
): $ReadOnlyArray<VariableUsage> {
|
||||
let usages = this._recursiveVariableUsages.get(operation);
|
||||
if (!usages) {
|
||||
usages = this.getVariableUsages(operation);
|
||||
const fragments = this.getRecursivelyReferencedFragments(operation);
|
||||
for (let i = 0; i < fragments.length; i++) {
|
||||
Array.prototype.push.apply(
|
||||
usages,
|
||||
this.getVariableUsages(fragments[i]),
|
||||
);
|
||||
}
|
||||
this._recursiveVariableUsages.set(operation, usages);
|
||||
}
|
||||
return usages;
|
||||
}
|
||||
|
||||
getType(): ?GraphQLOutputType {
|
||||
return this._typeInfo.getType();
|
||||
}
|
||||
|
||||
getParentType(): ?GraphQLCompositeType {
|
||||
return this._typeInfo.getParentType();
|
||||
}
|
||||
|
||||
getInputType(): ?GraphQLInputType {
|
||||
return this._typeInfo.getInputType();
|
||||
}
|
||||
|
||||
getParentInputType(): ?GraphQLInputType {
|
||||
return this._typeInfo.getParentInputType();
|
||||
}
|
||||
|
||||
getFieldDef(): ?GraphQLField<*, *> {
|
||||
return this._typeInfo.getFieldDef();
|
||||
}
|
||||
|
||||
getDirective(): ?GraphQLDirective {
|
||||
return this._typeInfo.getDirective();
|
||||
}
|
||||
|
||||
getArgument(): ?GraphQLArgument {
|
||||
return this._typeInfo.getArgument();
|
||||
}
|
||||
}
|
||||
|
||||
export type ValidationRule = ValidationContext => ASTVisitor;
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
|
||||
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { visit, visitWithTypeInfo } from '../language/visitor';
|
||||
import { Kind } from '../language/kinds';
|
||||
import { TypeInfo } from '../utilities/TypeInfo';
|
||||
|
||||
/**
|
||||
* An instance of this class is passed as the "this" context to all validators,
|
||||
* allowing access to commonly useful contextual information from within a
|
||||
* validation rule.
|
||||
*/
|
||||
export var ASTValidationContext =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function ASTValidationContext(ast) {
|
||||
_defineProperty(this, "_ast", void 0);
|
||||
|
||||
_defineProperty(this, "_errors", void 0);
|
||||
|
||||
this._ast = ast;
|
||||
this._errors = [];
|
||||
}
|
||||
|
||||
var _proto = ASTValidationContext.prototype;
|
||||
|
||||
_proto.reportError = function reportError(error) {
|
||||
this._errors.push(error);
|
||||
};
|
||||
|
||||
_proto.getErrors = function getErrors() {
|
||||
return this._errors;
|
||||
};
|
||||
|
||||
_proto.getDocument = function getDocument() {
|
||||
return this._ast;
|
||||
};
|
||||
|
||||
return ASTValidationContext;
|
||||
}();
|
||||
export var SDLValidationContext =
|
||||
/*#__PURE__*/
|
||||
function (_ASTValidationContext) {
|
||||
_inheritsLoose(SDLValidationContext, _ASTValidationContext);
|
||||
|
||||
function SDLValidationContext(ast, schema) {
|
||||
var _this;
|
||||
|
||||
_this = _ASTValidationContext.call(this, ast) || this;
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "_schema", void 0);
|
||||
|
||||
_this._schema = schema;
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto2 = SDLValidationContext.prototype;
|
||||
|
||||
_proto2.getSchema = function getSchema() {
|
||||
return this._schema;
|
||||
};
|
||||
|
||||
return SDLValidationContext;
|
||||
}(ASTValidationContext);
|
||||
export var ValidationContext =
|
||||
/*#__PURE__*/
|
||||
function (_ASTValidationContext2) {
|
||||
_inheritsLoose(ValidationContext, _ASTValidationContext2);
|
||||
|
||||
function ValidationContext(schema, ast, typeInfo) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _ASTValidationContext2.call(this, ast) || this;
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_schema", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_typeInfo", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_fragments", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_fragmentSpreads", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_recursivelyReferencedFragments", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_variableUsages", void 0);
|
||||
|
||||
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this2)), "_recursiveVariableUsages", void 0);
|
||||
|
||||
_this2._schema = schema;
|
||||
_this2._typeInfo = typeInfo;
|
||||
_this2._fragmentSpreads = new Map();
|
||||
_this2._recursivelyReferencedFragments = new Map();
|
||||
_this2._variableUsages = new Map();
|
||||
_this2._recursiveVariableUsages = new Map();
|
||||
return _this2;
|
||||
}
|
||||
|
||||
var _proto3 = ValidationContext.prototype;
|
||||
|
||||
_proto3.getSchema = function getSchema() {
|
||||
return this._schema;
|
||||
};
|
||||
|
||||
_proto3.getFragment = function getFragment(name) {
|
||||
var fragments = this._fragments;
|
||||
|
||||
if (!fragments) {
|
||||
this._fragments = fragments = this.getDocument().definitions.reduce(function (frags, statement) {
|
||||
if (statement.kind === Kind.FRAGMENT_DEFINITION) {
|
||||
frags[statement.name.value] = statement;
|
||||
}
|
||||
|
||||
return frags;
|
||||
}, Object.create(null));
|
||||
}
|
||||
|
||||
return fragments[name];
|
||||
};
|
||||
|
||||
_proto3.getFragmentSpreads = function getFragmentSpreads(node) {
|
||||
var spreads = this._fragmentSpreads.get(node);
|
||||
|
||||
if (!spreads) {
|
||||
spreads = [];
|
||||
var setsToVisit = [node];
|
||||
|
||||
while (setsToVisit.length !== 0) {
|
||||
var set = setsToVisit.pop();
|
||||
|
||||
for (var i = 0; i < set.selections.length; i++) {
|
||||
var selection = set.selections[i];
|
||||
|
||||
if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
||||
spreads.push(selection);
|
||||
} else if (selection.selectionSet) {
|
||||
setsToVisit.push(selection.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._fragmentSpreads.set(node, spreads);
|
||||
}
|
||||
|
||||
return spreads;
|
||||
};
|
||||
|
||||
_proto3.getRecursivelyReferencedFragments = function getRecursivelyReferencedFragments(operation) {
|
||||
var fragments = this._recursivelyReferencedFragments.get(operation);
|
||||
|
||||
if (!fragments) {
|
||||
fragments = [];
|
||||
var collectedNames = Object.create(null);
|
||||
var nodesToVisit = [operation.selectionSet];
|
||||
|
||||
while (nodesToVisit.length !== 0) {
|
||||
var node = nodesToVisit.pop();
|
||||
var spreads = this.getFragmentSpreads(node);
|
||||
|
||||
for (var i = 0; i < spreads.length; i++) {
|
||||
var fragName = spreads[i].name.value;
|
||||
|
||||
if (collectedNames[fragName] !== true) {
|
||||
collectedNames[fragName] = true;
|
||||
var fragment = this.getFragment(fragName);
|
||||
|
||||
if (fragment) {
|
||||
fragments.push(fragment);
|
||||
nodesToVisit.push(fragment.selectionSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._recursivelyReferencedFragments.set(operation, fragments);
|
||||
}
|
||||
|
||||
return fragments;
|
||||
};
|
||||
|
||||
_proto3.getVariableUsages = function getVariableUsages(node) {
|
||||
var usages = this._variableUsages.get(node);
|
||||
|
||||
if (!usages) {
|
||||
var newUsages = [];
|
||||
var typeInfo = new TypeInfo(this._schema);
|
||||
visit(node, visitWithTypeInfo(typeInfo, {
|
||||
VariableDefinition: function VariableDefinition() {
|
||||
return false;
|
||||
},
|
||||
Variable: function Variable(variable) {
|
||||
newUsages.push({
|
||||
node: variable,
|
||||
type: typeInfo.getInputType(),
|
||||
defaultValue: typeInfo.getDefaultValue()
|
||||
});
|
||||
}
|
||||
}));
|
||||
usages = newUsages;
|
||||
|
||||
this._variableUsages.set(node, usages);
|
||||
}
|
||||
|
||||
return usages;
|
||||
};
|
||||
|
||||
_proto3.getRecursiveVariableUsages = function getRecursiveVariableUsages(operation) {
|
||||
var usages = this._recursiveVariableUsages.get(operation);
|
||||
|
||||
if (!usages) {
|
||||
usages = this.getVariableUsages(operation);
|
||||
var fragments = this.getRecursivelyReferencedFragments(operation);
|
||||
|
||||
for (var i = 0; i < fragments.length; i++) {
|
||||
Array.prototype.push.apply(usages, this.getVariableUsages(fragments[i]));
|
||||
}
|
||||
|
||||
this._recursiveVariableUsages.set(operation, usages);
|
||||
}
|
||||
|
||||
return usages;
|
||||
};
|
||||
|
||||
_proto3.getType = function getType() {
|
||||
return this._typeInfo.getType();
|
||||
};
|
||||
|
||||
_proto3.getParentType = function getParentType() {
|
||||
return this._typeInfo.getParentType();
|
||||
};
|
||||
|
||||
_proto3.getInputType = function getInputType() {
|
||||
return this._typeInfo.getInputType();
|
||||
};
|
||||
|
||||
_proto3.getParentInputType = function getParentInputType() {
|
||||
return this._typeInfo.getParentInputType();
|
||||
};
|
||||
|
||||
_proto3.getFieldDef = function getFieldDef() {
|
||||
return this._typeInfo.getFieldDef();
|
||||
};
|
||||
|
||||
_proto3.getDirective = function getDirective() {
|
||||
return this._typeInfo.getDirective();
|
||||
};
|
||||
|
||||
_proto3.getArgument = function getArgument() {
|
||||
return this._typeInfo.getArgument();
|
||||
};
|
||||
|
||||
return ValidationContext;
|
||||
}(ASTValidationContext);
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "validate", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _validate.validate;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "ValidationContext", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _ValidationContext.ValidationContext;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "specifiedRules", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _specifiedRules.specifiedRules;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "FieldsOnCorrectTypeRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _FieldsOnCorrectType.FieldsOnCorrectType;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "FragmentsOnCompositeTypesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _FragmentsOnCompositeTypes.FragmentsOnCompositeTypes;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "KnownArgumentNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _KnownArgumentNames.KnownArgumentNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "KnownDirectivesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _KnownDirectives.KnownDirectives;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "KnownFragmentNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _KnownFragmentNames.KnownFragmentNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "KnownTypeNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _KnownTypeNames.KnownTypeNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "LoneAnonymousOperationRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _LoneAnonymousOperation.LoneAnonymousOperation;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "NoFragmentCyclesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _NoFragmentCycles.NoFragmentCycles;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "NoUndefinedVariablesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _NoUndefinedVariables.NoUndefinedVariables;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "NoUnusedFragmentsRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _NoUnusedFragments.NoUnusedFragments;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "NoUnusedVariablesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _NoUnusedVariables.NoUnusedVariables;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "OverlappingFieldsCanBeMergedRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _OverlappingFieldsCanBeMerged.OverlappingFieldsCanBeMerged;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "PossibleFragmentSpreadsRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _PossibleFragmentSpreads.PossibleFragmentSpreads;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "ProvidedRequiredArgumentsRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _ProvidedRequiredArguments.ProvidedRequiredArguments;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "ScalarLeafsRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _ScalarLeafs.ScalarLeafs;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "SingleFieldSubscriptionsRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _SingleFieldSubscriptions.SingleFieldSubscriptions;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueArgumentNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueArgumentNames.UniqueArgumentNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueDirectivesPerLocationRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueDirectivesPerLocation.UniqueDirectivesPerLocation;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueFragmentNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueFragmentNames.UniqueFragmentNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueInputFieldNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueInputFieldNames.UniqueInputFieldNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueOperationNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueOperationNames.UniqueOperationNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "UniqueVariableNamesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _UniqueVariableNames.UniqueVariableNames;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "ValuesOfCorrectTypeRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _ValuesOfCorrectType.ValuesOfCorrectType;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "VariablesAreInputTypesRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _VariablesAreInputTypes.VariablesAreInputTypes;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "VariablesInAllowedPositionRule", {
|
||||
enumerable: true,
|
||||
get: function get() {
|
||||
return _VariablesInAllowedPosition.VariablesInAllowedPosition;
|
||||
}
|
||||
});
|
||||
|
||||
var _validate = require("./validate");
|
||||
|
||||
var _ValidationContext = require("./ValidationContext");
|
||||
|
||||
var _specifiedRules = require("./specifiedRules");
|
||||
|
||||
var _FieldsOnCorrectType = require("./rules/FieldsOnCorrectType");
|
||||
|
||||
var _FragmentsOnCompositeTypes = require("./rules/FragmentsOnCompositeTypes");
|
||||
|
||||
var _KnownArgumentNames = require("./rules/KnownArgumentNames");
|
||||
|
||||
var _KnownDirectives = require("./rules/KnownDirectives");
|
||||
|
||||
var _KnownFragmentNames = require("./rules/KnownFragmentNames");
|
||||
|
||||
var _KnownTypeNames = require("./rules/KnownTypeNames");
|
||||
|
||||
var _LoneAnonymousOperation = require("./rules/LoneAnonymousOperation");
|
||||
|
||||
var _NoFragmentCycles = require("./rules/NoFragmentCycles");
|
||||
|
||||
var _NoUndefinedVariables = require("./rules/NoUndefinedVariables");
|
||||
|
||||
var _NoUnusedFragments = require("./rules/NoUnusedFragments");
|
||||
|
||||
var _NoUnusedVariables = require("./rules/NoUnusedVariables");
|
||||
|
||||
var _OverlappingFieldsCanBeMerged = require("./rules/OverlappingFieldsCanBeMerged");
|
||||
|
||||
var _PossibleFragmentSpreads = require("./rules/PossibleFragmentSpreads");
|
||||
|
||||
var _ProvidedRequiredArguments = require("./rules/ProvidedRequiredArguments");
|
||||
|
||||
var _ScalarLeafs = require("./rules/ScalarLeafs");
|
||||
|
||||
var _SingleFieldSubscriptions = require("./rules/SingleFieldSubscriptions");
|
||||
|
||||
var _UniqueArgumentNames = require("./rules/UniqueArgumentNames");
|
||||
|
||||
var _UniqueDirectivesPerLocation = require("./rules/UniqueDirectivesPerLocation");
|
||||
|
||||
var _UniqueFragmentNames = require("./rules/UniqueFragmentNames");
|
||||
|
||||
var _UniqueInputFieldNames = require("./rules/UniqueInputFieldNames");
|
||||
|
||||
var _UniqueOperationNames = require("./rules/UniqueOperationNames");
|
||||
|
||||
var _UniqueVariableNames = require("./rules/UniqueVariableNames");
|
||||
|
||||
var _ValuesOfCorrectType = require("./rules/ValuesOfCorrectType");
|
||||
|
||||
var _VariablesAreInputTypes = require("./rules/VariablesAreInputTypes");
|
||||
|
||||
var _VariablesInAllowedPosition = require("./rules/VariablesInAllowedPosition");
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
export { validate } from './validate';
|
||||
|
||||
export { ValidationContext } from './ValidationContext';
|
||||
|
||||
export { specifiedRules } from './specifiedRules';
|
||||
|
||||
// Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
|
||||
export {
|
||||
FieldsOnCorrectType as FieldsOnCorrectTypeRule,
|
||||
} from './rules/FieldsOnCorrectType';
|
||||
|
||||
// Spec Section: "Fragments on Composite Types"
|
||||
export {
|
||||
FragmentsOnCompositeTypes as FragmentsOnCompositeTypesRule,
|
||||
} from './rules/FragmentsOnCompositeTypes';
|
||||
|
||||
// Spec Section: "Argument Names"
|
||||
export {
|
||||
KnownArgumentNames as KnownArgumentNamesRule,
|
||||
} from './rules/KnownArgumentNames';
|
||||
|
||||
// Spec Section: "Directives Are Defined"
|
||||
export {
|
||||
KnownDirectives as KnownDirectivesRule,
|
||||
} from './rules/KnownDirectives';
|
||||
|
||||
// Spec Section: "Fragment spread target defined"
|
||||
export {
|
||||
KnownFragmentNames as KnownFragmentNamesRule,
|
||||
} from './rules/KnownFragmentNames';
|
||||
|
||||
// Spec Section: "Fragment Spread Type Existence"
|
||||
export { KnownTypeNames as KnownTypeNamesRule } from './rules/KnownTypeNames';
|
||||
|
||||
// Spec Section: "Lone Anonymous Operation"
|
||||
export {
|
||||
LoneAnonymousOperation as LoneAnonymousOperationRule,
|
||||
} from './rules/LoneAnonymousOperation';
|
||||
|
||||
// Spec Section: "Fragments must not form cycles"
|
||||
export {
|
||||
NoFragmentCycles as NoFragmentCyclesRule,
|
||||
} from './rules/NoFragmentCycles';
|
||||
|
||||
// Spec Section: "All Variable Used Defined"
|
||||
export {
|
||||
NoUndefinedVariables as NoUndefinedVariablesRule,
|
||||
} from './rules/NoUndefinedVariables';
|
||||
|
||||
// Spec Section: "Fragments must be used"
|
||||
export {
|
||||
NoUnusedFragments as NoUnusedFragmentsRule,
|
||||
} from './rules/NoUnusedFragments';
|
||||
|
||||
// Spec Section: "All Variables Used"
|
||||
export {
|
||||
NoUnusedVariables as NoUnusedVariablesRule,
|
||||
} from './rules/NoUnusedVariables';
|
||||
|
||||
// Spec Section: "Field Selection Merging"
|
||||
export {
|
||||
OverlappingFieldsCanBeMerged as OverlappingFieldsCanBeMergedRule,
|
||||
} from './rules/OverlappingFieldsCanBeMerged';
|
||||
|
||||
// Spec Section: "Fragment spread is possible"
|
||||
export {
|
||||
PossibleFragmentSpreads as PossibleFragmentSpreadsRule,
|
||||
} from './rules/PossibleFragmentSpreads';
|
||||
|
||||
// Spec Section: "Argument Optionality"
|
||||
export {
|
||||
ProvidedRequiredArguments as ProvidedRequiredArgumentsRule,
|
||||
} from './rules/ProvidedRequiredArguments';
|
||||
|
||||
// Spec Section: "Leaf Field Selections"
|
||||
export { ScalarLeafs as ScalarLeafsRule } from './rules/ScalarLeafs';
|
||||
|
||||
// Spec Section: "Subscriptions with Single Root Field"
|
||||
export {
|
||||
SingleFieldSubscriptions as SingleFieldSubscriptionsRule,
|
||||
} from './rules/SingleFieldSubscriptions';
|
||||
|
||||
// Spec Section: "Argument Uniqueness"
|
||||
export {
|
||||
UniqueArgumentNames as UniqueArgumentNamesRule,
|
||||
} from './rules/UniqueArgumentNames';
|
||||
|
||||
// Spec Section: "Directives Are Unique Per Location"
|
||||
export {
|
||||
UniqueDirectivesPerLocation as UniqueDirectivesPerLocationRule,
|
||||
} from './rules/UniqueDirectivesPerLocation';
|
||||
|
||||
// Spec Section: "Fragment Name Uniqueness"
|
||||
export {
|
||||
UniqueFragmentNames as UniqueFragmentNamesRule,
|
||||
} from './rules/UniqueFragmentNames';
|
||||
|
||||
// Spec Section: "Input Object Field Uniqueness"
|
||||
export {
|
||||
UniqueInputFieldNames as UniqueInputFieldNamesRule,
|
||||
} from './rules/UniqueInputFieldNames';
|
||||
|
||||
// Spec Section: "Operation Name Uniqueness"
|
||||
export {
|
||||
UniqueOperationNames as UniqueOperationNamesRule,
|
||||
} from './rules/UniqueOperationNames';
|
||||
|
||||
// Spec Section: "Variable Uniqueness"
|
||||
export {
|
||||
UniqueVariableNames as UniqueVariableNamesRule,
|
||||
} from './rules/UniqueVariableNames';
|
||||
|
||||
// Spec Section: "Values Type Correctness"
|
||||
export {
|
||||
ValuesOfCorrectType as ValuesOfCorrectTypeRule,
|
||||
} from './rules/ValuesOfCorrectType';
|
||||
|
||||
// Spec Section: "Variables are Input Types"
|
||||
export {
|
||||
VariablesAreInputTypes as VariablesAreInputTypesRule,
|
||||
} from './rules/VariablesAreInputTypes';
|
||||
|
||||
// Spec Section: "All Variable Usages Are Allowed"
|
||||
export {
|
||||
VariablesInAllowedPosition as VariablesInAllowedPositionRule,
|
||||
} from './rules/VariablesInAllowedPosition';
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
export { validate } from './validate';
|
||||
export { ValidationContext } from './ValidationContext';
|
||||
export { specifiedRules } from './specifiedRules'; // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
|
||||
|
||||
export { FieldsOnCorrectType as FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectType'; // Spec Section: "Fragments on Composite Types"
|
||||
|
||||
export { FragmentsOnCompositeTypes as FragmentsOnCompositeTypesRule } from './rules/FragmentsOnCompositeTypes'; // Spec Section: "Argument Names"
|
||||
|
||||
export { KnownArgumentNames as KnownArgumentNamesRule } from './rules/KnownArgumentNames'; // Spec Section: "Directives Are Defined"
|
||||
|
||||
export { KnownDirectives as KnownDirectivesRule } from './rules/KnownDirectives'; // Spec Section: "Fragment spread target defined"
|
||||
|
||||
export { KnownFragmentNames as KnownFragmentNamesRule } from './rules/KnownFragmentNames'; // Spec Section: "Fragment Spread Type Existence"
|
||||
|
||||
export { KnownTypeNames as KnownTypeNamesRule } from './rules/KnownTypeNames'; // Spec Section: "Lone Anonymous Operation"
|
||||
|
||||
export { LoneAnonymousOperation as LoneAnonymousOperationRule } from './rules/LoneAnonymousOperation'; // Spec Section: "Fragments must not form cycles"
|
||||
|
||||
export { NoFragmentCycles as NoFragmentCyclesRule } from './rules/NoFragmentCycles'; // Spec Section: "All Variable Used Defined"
|
||||
|
||||
export { NoUndefinedVariables as NoUndefinedVariablesRule } from './rules/NoUndefinedVariables'; // Spec Section: "Fragments must be used"
|
||||
|
||||
export { NoUnusedFragments as NoUnusedFragmentsRule } from './rules/NoUnusedFragments'; // Spec Section: "All Variables Used"
|
||||
|
||||
export { NoUnusedVariables as NoUnusedVariablesRule } from './rules/NoUnusedVariables'; // Spec Section: "Field Selection Merging"
|
||||
|
||||
export { OverlappingFieldsCanBeMerged as OverlappingFieldsCanBeMergedRule } from './rules/OverlappingFieldsCanBeMerged'; // Spec Section: "Fragment spread is possible"
|
||||
|
||||
export { PossibleFragmentSpreads as PossibleFragmentSpreadsRule } from './rules/PossibleFragmentSpreads'; // Spec Section: "Argument Optionality"
|
||||
|
||||
export { ProvidedRequiredArguments as ProvidedRequiredArgumentsRule } from './rules/ProvidedRequiredArguments'; // Spec Section: "Leaf Field Selections"
|
||||
|
||||
export { ScalarLeafs as ScalarLeafsRule } from './rules/ScalarLeafs'; // Spec Section: "Subscriptions with Single Root Field"
|
||||
|
||||
export { SingleFieldSubscriptions as SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptions'; // Spec Section: "Argument Uniqueness"
|
||||
|
||||
export { UniqueArgumentNames as UniqueArgumentNamesRule } from './rules/UniqueArgumentNames'; // Spec Section: "Directives Are Unique Per Location"
|
||||
|
||||
export { UniqueDirectivesPerLocation as UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocation'; // Spec Section: "Fragment Name Uniqueness"
|
||||
|
||||
export { UniqueFragmentNames as UniqueFragmentNamesRule } from './rules/UniqueFragmentNames'; // Spec Section: "Input Object Field Uniqueness"
|
||||
|
||||
export { UniqueInputFieldNames as UniqueInputFieldNamesRule } from './rules/UniqueInputFieldNames'; // Spec Section: "Operation Name Uniqueness"
|
||||
|
||||
export { UniqueOperationNames as UniqueOperationNamesRule } from './rules/UniqueOperationNames'; // Spec Section: "Variable Uniqueness"
|
||||
|
||||
export { UniqueVariableNames as UniqueVariableNamesRule } from './rules/UniqueVariableNames'; // Spec Section: "Values Type Correctness"
|
||||
|
||||
export { ValuesOfCorrectType as ValuesOfCorrectTypeRule } from './rules/ValuesOfCorrectType'; // Spec Section: "Variables are Input Types"
|
||||
|
||||
export { VariablesAreInputTypes as VariablesAreInputTypesRule } from './rules/VariablesAreInputTypes'; // Spec Section: "All Variable Usages Are Allowed"
|
||||
|
||||
export { VariablesInAllowedPosition as VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPosition';
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.nonExecutableDefinitionMessage = nonExecutableDefinitionMessage;
|
||||
exports.ExecutableDefinitions = ExecutableDefinitions;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _predicates = require("../../language/predicates");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function nonExecutableDefinitionMessage(defName) {
|
||||
return "The ".concat(defName, " definition is not executable.");
|
||||
}
|
||||
/**
|
||||
* Executable definitions
|
||||
*
|
||||
* A GraphQL document is only valid for execution if all definitions are either
|
||||
* operation or fragment definitions.
|
||||
*/
|
||||
|
||||
|
||||
function ExecutableDefinitions(context) {
|
||||
return {
|
||||
Document: function Document(node) {
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = node.definitions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var definition = _step.value;
|
||||
|
||||
if (!(0, _predicates.isExecutableDefinitionNode)(definition)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(nonExecutableDefinitionMessage(definition.kind === _kinds.Kind.SCHEMA_DEFINITION || definition.kind === _kinds.Kind.SCHEMA_EXTENSION ? 'schema' : definition.name.value), [definition]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { isExecutableDefinitionNode } from '../../language/predicates';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function nonExecutableDefinitionMessage(defName: string): string {
|
||||
return `The ${defName} definition is not executable.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executable definitions
|
||||
*
|
||||
* A GraphQL document is only valid for execution if all definitions are either
|
||||
* operation or fragment definitions.
|
||||
*/
|
||||
export function ExecutableDefinitions(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
Document(node) {
|
||||
for (const definition of node.definitions) {
|
||||
if (!isExecutableDefinitionNode(definition)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
nonExecutableDefinitionMessage(
|
||||
definition.kind === Kind.SCHEMA_DEFINITION ||
|
||||
definition.kind === Kind.SCHEMA_EXTENSION
|
||||
? 'schema'
|
||||
: definition.name.value,
|
||||
),
|
||||
[definition],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { isExecutableDefinitionNode } from '../../language/predicates';
|
||||
export function nonExecutableDefinitionMessage(defName) {
|
||||
return "The ".concat(defName, " definition is not executable.");
|
||||
}
|
||||
/**
|
||||
* Executable definitions
|
||||
*
|
||||
* A GraphQL document is only valid for execution if all definitions are either
|
||||
* operation or fragment definitions.
|
||||
*/
|
||||
|
||||
export function ExecutableDefinitions(context) {
|
||||
return {
|
||||
Document: function Document(node) {
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = node.definitions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var definition = _step.value;
|
||||
|
||||
if (!isExecutableDefinitionNode(definition)) {
|
||||
context.reportError(new GraphQLError(nonExecutableDefinitionMessage(definition.kind === Kind.SCHEMA_DEFINITION || definition.kind === Kind.SCHEMA_EXTENSION ? 'schema' : definition.name.value), [definition]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.undefinedFieldMessage = undefinedFieldMessage;
|
||||
exports.FieldsOnCorrectType = FieldsOnCorrectType;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _suggestionList = _interopRequireDefault(require("../../jsutils/suggestionList"));
|
||||
|
||||
var _quotedOrList = _interopRequireDefault(require("../../jsutils/quotedOrList"));
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function undefinedFieldMessage(fieldName, type, suggestedTypeNames, suggestedFieldNames) {
|
||||
var message = "Cannot query field \"".concat(fieldName, "\" on type \"").concat(type, "\".");
|
||||
|
||||
if (suggestedTypeNames.length !== 0) {
|
||||
var suggestions = (0, _quotedOrList.default)(suggestedTypeNames);
|
||||
message += " Did you mean to use an inline fragment on ".concat(suggestions, "?");
|
||||
} else if (suggestedFieldNames.length !== 0) {
|
||||
message += " Did you mean ".concat((0, _quotedOrList.default)(suggestedFieldNames), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Fields on correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all fields selected are defined by the
|
||||
* parent type, or are an allowed meta field such as __typename.
|
||||
*/
|
||||
|
||||
|
||||
function FieldsOnCorrectType(context) {
|
||||
return {
|
||||
Field: function Field(node) {
|
||||
var type = context.getParentType();
|
||||
|
||||
if (type) {
|
||||
var fieldDef = context.getFieldDef();
|
||||
|
||||
if (!fieldDef) {
|
||||
// This field doesn't exist, lets look for suggestions.
|
||||
var schema = context.getSchema();
|
||||
var fieldName = node.name.value; // First determine if there are any suggested types to condition on.
|
||||
|
||||
var suggestedTypeNames = getSuggestedTypeNames(schema, type, fieldName); // If there are no suggested types, then perhaps this was a typo?
|
||||
|
||||
var suggestedFieldNames = suggestedTypeNames.length !== 0 ? [] : getSuggestedFieldNames(schema, type, fieldName); // Report an error, including helpful suggestions.
|
||||
|
||||
context.reportError(new _GraphQLError.GraphQLError(undefinedFieldMessage(fieldName, type.name, suggestedTypeNames, suggestedFieldNames), [node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Go through all of the implementations of type, as well as the interfaces that
|
||||
* they implement. If any of those types include the provided field, suggest
|
||||
* them, sorted by how often the type is referenced, starting with Interfaces.
|
||||
*/
|
||||
|
||||
|
||||
function getSuggestedTypeNames(schema, type, fieldName) {
|
||||
if ((0, _definition.isAbstractType)(type)) {
|
||||
var suggestedObjectTypes = [];
|
||||
var interfaceUsageCount = Object.create(null);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = schema.getPossibleTypes(type)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var possibleType = _step.value;
|
||||
|
||||
if (!possibleType.getFields()[fieldName]) {
|
||||
continue;
|
||||
} // This object type defines this field.
|
||||
|
||||
|
||||
suggestedObjectTypes.push(possibleType.name);
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = possibleType.getInterfaces()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var possibleInterface = _step2.value;
|
||||
|
||||
if (!possibleInterface.getFields()[fieldName]) {
|
||||
continue;
|
||||
} // This interface type defines this field.
|
||||
|
||||
|
||||
interfaceUsageCount[possibleInterface.name] = (interfaceUsageCount[possibleInterface.name] || 0) + 1;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Suggest interface types based on how common they are.
|
||||
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var suggestedInterfaceTypes = Object.keys(interfaceUsageCount).sort(function (a, b) {
|
||||
return interfaceUsageCount[b] - interfaceUsageCount[a];
|
||||
}); // Suggest both interface and object types.
|
||||
|
||||
return suggestedInterfaceTypes.concat(suggestedObjectTypes);
|
||||
} // Otherwise, must be an Object type, which does not have possible fields.
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* For the field name provided, determine if there are any similar field names
|
||||
* that may be the result of a typo.
|
||||
*/
|
||||
|
||||
|
||||
function getSuggestedFieldNames(schema, type, fieldName) {
|
||||
if ((0, _definition.isObjectType)(type) || (0, _definition.isInterfaceType)(type)) {
|
||||
var possibleFieldNames = Object.keys(type.getFields());
|
||||
return (0, _suggestionList.default)(fieldName, possibleFieldNames);
|
||||
} // Otherwise, must be a Union type, which does not define fields.
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
import type { FieldNode } from '../../language/ast';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import type { GraphQLSchema } from '../../type/schema';
|
||||
import type { GraphQLOutputType } from '../../type/definition';
|
||||
import {
|
||||
isObjectType,
|
||||
isInterfaceType,
|
||||
isAbstractType,
|
||||
} from '../../type/definition';
|
||||
|
||||
export function undefinedFieldMessage(
|
||||
fieldName: string,
|
||||
type: string,
|
||||
suggestedTypeNames: Array<string>,
|
||||
suggestedFieldNames: Array<string>,
|
||||
): string {
|
||||
let message = `Cannot query field "${fieldName}" on type "${type}".`;
|
||||
if (suggestedTypeNames.length !== 0) {
|
||||
const suggestions = quotedOrList(suggestedTypeNames);
|
||||
message += ` Did you mean to use an inline fragment on ${suggestions}?`;
|
||||
} else if (suggestedFieldNames.length !== 0) {
|
||||
message += ` Did you mean ${quotedOrList(suggestedFieldNames)}?`;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields on correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all fields selected are defined by the
|
||||
* parent type, or are an allowed meta field such as __typename.
|
||||
*/
|
||||
export function FieldsOnCorrectType(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
Field(node: FieldNode) {
|
||||
const type = context.getParentType();
|
||||
if (type) {
|
||||
const fieldDef = context.getFieldDef();
|
||||
if (!fieldDef) {
|
||||
// This field doesn't exist, lets look for suggestions.
|
||||
const schema = context.getSchema();
|
||||
const fieldName = node.name.value;
|
||||
// First determine if there are any suggested types to condition on.
|
||||
const suggestedTypeNames = getSuggestedTypeNames(
|
||||
schema,
|
||||
type,
|
||||
fieldName,
|
||||
);
|
||||
// If there are no suggested types, then perhaps this was a typo?
|
||||
const suggestedFieldNames =
|
||||
suggestedTypeNames.length !== 0
|
||||
? []
|
||||
: getSuggestedFieldNames(schema, type, fieldName);
|
||||
|
||||
// Report an error, including helpful suggestions.
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
undefinedFieldMessage(
|
||||
fieldName,
|
||||
type.name,
|
||||
suggestedTypeNames,
|
||||
suggestedFieldNames,
|
||||
),
|
||||
[node],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all of the implementations of type, as well as the interfaces that
|
||||
* they implement. If any of those types include the provided field, suggest
|
||||
* them, sorted by how often the type is referenced, starting with Interfaces.
|
||||
*/
|
||||
function getSuggestedTypeNames(
|
||||
schema: GraphQLSchema,
|
||||
type: GraphQLOutputType,
|
||||
fieldName: string,
|
||||
): Array<string> {
|
||||
if (isAbstractType(type)) {
|
||||
const suggestedObjectTypes = [];
|
||||
const interfaceUsageCount = Object.create(null);
|
||||
for (const possibleType of schema.getPossibleTypes(type)) {
|
||||
if (!possibleType.getFields()[fieldName]) {
|
||||
continue;
|
||||
}
|
||||
// This object type defines this field.
|
||||
suggestedObjectTypes.push(possibleType.name);
|
||||
for (const possibleInterface of possibleType.getInterfaces()) {
|
||||
if (!possibleInterface.getFields()[fieldName]) {
|
||||
continue;
|
||||
}
|
||||
// This interface type defines this field.
|
||||
interfaceUsageCount[possibleInterface.name] =
|
||||
(interfaceUsageCount[possibleInterface.name] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Suggest interface types based on how common they are.
|
||||
const suggestedInterfaceTypes = Object.keys(interfaceUsageCount).sort(
|
||||
(a, b) => interfaceUsageCount[b] - interfaceUsageCount[a],
|
||||
);
|
||||
|
||||
// Suggest both interface and object types.
|
||||
return suggestedInterfaceTypes.concat(suggestedObjectTypes);
|
||||
}
|
||||
|
||||
// Otherwise, must be an Object type, which does not have possible fields.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* For the field name provided, determine if there are any similar field names
|
||||
* that may be the result of a typo.
|
||||
*/
|
||||
function getSuggestedFieldNames(
|
||||
schema: GraphQLSchema,
|
||||
type: GraphQLOutputType,
|
||||
fieldName: string,
|
||||
): Array<string> {
|
||||
if (isObjectType(type) || isInterfaceType(type)) {
|
||||
const possibleFieldNames = Object.keys(type.getFields());
|
||||
return suggestionList(fieldName, possibleFieldNames);
|
||||
}
|
||||
// Otherwise, must be a Union type, which does not define fields.
|
||||
return [];
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
import { isObjectType, isInterfaceType, isAbstractType } from '../../type/definition';
|
||||
export function undefinedFieldMessage(fieldName, type, suggestedTypeNames, suggestedFieldNames) {
|
||||
var message = "Cannot query field \"".concat(fieldName, "\" on type \"").concat(type, "\".");
|
||||
|
||||
if (suggestedTypeNames.length !== 0) {
|
||||
var suggestions = quotedOrList(suggestedTypeNames);
|
||||
message += " Did you mean to use an inline fragment on ".concat(suggestions, "?");
|
||||
} else if (suggestedFieldNames.length !== 0) {
|
||||
message += " Did you mean ".concat(quotedOrList(suggestedFieldNames), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Fields on correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all fields selected are defined by the
|
||||
* parent type, or are an allowed meta field such as __typename.
|
||||
*/
|
||||
|
||||
export function FieldsOnCorrectType(context) {
|
||||
return {
|
||||
Field: function Field(node) {
|
||||
var type = context.getParentType();
|
||||
|
||||
if (type) {
|
||||
var fieldDef = context.getFieldDef();
|
||||
|
||||
if (!fieldDef) {
|
||||
// This field doesn't exist, lets look for suggestions.
|
||||
var schema = context.getSchema();
|
||||
var fieldName = node.name.value; // First determine if there are any suggested types to condition on.
|
||||
|
||||
var suggestedTypeNames = getSuggestedTypeNames(schema, type, fieldName); // If there are no suggested types, then perhaps this was a typo?
|
||||
|
||||
var suggestedFieldNames = suggestedTypeNames.length !== 0 ? [] : getSuggestedFieldNames(schema, type, fieldName); // Report an error, including helpful suggestions.
|
||||
|
||||
context.reportError(new GraphQLError(undefinedFieldMessage(fieldName, type.name, suggestedTypeNames, suggestedFieldNames), [node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Go through all of the implementations of type, as well as the interfaces that
|
||||
* they implement. If any of those types include the provided field, suggest
|
||||
* them, sorted by how often the type is referenced, starting with Interfaces.
|
||||
*/
|
||||
|
||||
function getSuggestedTypeNames(schema, type, fieldName) {
|
||||
if (isAbstractType(type)) {
|
||||
var suggestedObjectTypes = [];
|
||||
var interfaceUsageCount = Object.create(null);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = schema.getPossibleTypes(type)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var possibleType = _step.value;
|
||||
|
||||
if (!possibleType.getFields()[fieldName]) {
|
||||
continue;
|
||||
} // This object type defines this field.
|
||||
|
||||
|
||||
suggestedObjectTypes.push(possibleType.name);
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = possibleType.getInterfaces()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var possibleInterface = _step2.value;
|
||||
|
||||
if (!possibleInterface.getFields()[fieldName]) {
|
||||
continue;
|
||||
} // This interface type defines this field.
|
||||
|
||||
|
||||
interfaceUsageCount[possibleInterface.name] = (interfaceUsageCount[possibleInterface.name] || 0) + 1;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Suggest interface types based on how common they are.
|
||||
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var suggestedInterfaceTypes = Object.keys(interfaceUsageCount).sort(function (a, b) {
|
||||
return interfaceUsageCount[b] - interfaceUsageCount[a];
|
||||
}); // Suggest both interface and object types.
|
||||
|
||||
return suggestedInterfaceTypes.concat(suggestedObjectTypes);
|
||||
} // Otherwise, must be an Object type, which does not have possible fields.
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* For the field name provided, determine if there are any similar field names
|
||||
* that may be the result of a typo.
|
||||
*/
|
||||
|
||||
|
||||
function getSuggestedFieldNames(schema, type, fieldName) {
|
||||
if (isObjectType(type) || isInterfaceType(type)) {
|
||||
var possibleFieldNames = Object.keys(type.getFields());
|
||||
return suggestionList(fieldName, possibleFieldNames);
|
||||
} // Otherwise, must be a Union type, which does not define fields.
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.inlineFragmentOnNonCompositeErrorMessage = inlineFragmentOnNonCompositeErrorMessage;
|
||||
exports.fragmentOnNonCompositeErrorMessage = fragmentOnNonCompositeErrorMessage;
|
||||
exports.FragmentsOnCompositeTypes = FragmentsOnCompositeTypes;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _printer = require("../../language/printer");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _typeFromAST = require("../../utilities/typeFromAST");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function inlineFragmentOnNonCompositeErrorMessage(type) {
|
||||
return "Fragment cannot condition on non composite type \"".concat(type, "\".");
|
||||
}
|
||||
|
||||
function fragmentOnNonCompositeErrorMessage(fragName, type) {
|
||||
return "Fragment \"".concat(fragName, "\" cannot condition on non composite ") + "type \"".concat(type, "\".");
|
||||
}
|
||||
/**
|
||||
* Fragments on composite type
|
||||
*
|
||||
* Fragments use a type condition to determine if they apply, since fragments
|
||||
* can only be spread into a composite type (object, interface, or union), the
|
||||
* type condition must also be a composite type.
|
||||
*/
|
||||
|
||||
|
||||
function FragmentsOnCompositeTypes(context) {
|
||||
return {
|
||||
InlineFragment: function InlineFragment(node) {
|
||||
var typeCondition = node.typeCondition;
|
||||
|
||||
if (typeCondition) {
|
||||
var type = (0, _typeFromAST.typeFromAST)(context.getSchema(), typeCondition);
|
||||
|
||||
if (type && !(0, _definition.isCompositeType)(type)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(inlineFragmentOnNonCompositeErrorMessage((0, _printer.print)(typeCondition)), [typeCondition]));
|
||||
}
|
||||
}
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
var type = (0, _typeFromAST.typeFromAST)(context.getSchema(), node.typeCondition);
|
||||
|
||||
if (type && !(0, _definition.isCompositeType)(type)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(fragmentOnNonCompositeErrorMessage(node.name.value, (0, _printer.print)(node.typeCondition)), [node.typeCondition]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { print } from '../../language/printer';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { isCompositeType } from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
|
||||
export function inlineFragmentOnNonCompositeErrorMessage(type: string): string {
|
||||
return `Fragment cannot condition on non composite type "${type}".`;
|
||||
}
|
||||
|
||||
export function fragmentOnNonCompositeErrorMessage(
|
||||
fragName: string,
|
||||
type: string,
|
||||
): string {
|
||||
return (
|
||||
`Fragment "${fragName}" cannot condition on non composite ` +
|
||||
`type "${type}".`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragments on composite type
|
||||
*
|
||||
* Fragments use a type condition to determine if they apply, since fragments
|
||||
* can only be spread into a composite type (object, interface, or union), the
|
||||
* type condition must also be a composite type.
|
||||
*/
|
||||
export function FragmentsOnCompositeTypes(
|
||||
context: ValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
InlineFragment(node) {
|
||||
const typeCondition = node.typeCondition;
|
||||
if (typeCondition) {
|
||||
const type = typeFromAST(context.getSchema(), typeCondition);
|
||||
if (type && !isCompositeType(type)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
inlineFragmentOnNonCompositeErrorMessage(print(typeCondition)),
|
||||
[typeCondition],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
FragmentDefinition(node) {
|
||||
const type = typeFromAST(context.getSchema(), node.typeCondition);
|
||||
if (type && !isCompositeType(type)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
fragmentOnNonCompositeErrorMessage(
|
||||
node.name.value,
|
||||
print(node.typeCondition),
|
||||
),
|
||||
[node.typeCondition],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { print } from '../../language/printer';
|
||||
import { isCompositeType } from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
export function inlineFragmentOnNonCompositeErrorMessage(type) {
|
||||
return "Fragment cannot condition on non composite type \"".concat(type, "\".");
|
||||
}
|
||||
export function fragmentOnNonCompositeErrorMessage(fragName, type) {
|
||||
return "Fragment \"".concat(fragName, "\" cannot condition on non composite ") + "type \"".concat(type, "\".");
|
||||
}
|
||||
/**
|
||||
* Fragments on composite type
|
||||
*
|
||||
* Fragments use a type condition to determine if they apply, since fragments
|
||||
* can only be spread into a composite type (object, interface, or union), the
|
||||
* type condition must also be a composite type.
|
||||
*/
|
||||
|
||||
export function FragmentsOnCompositeTypes(context) {
|
||||
return {
|
||||
InlineFragment: function InlineFragment(node) {
|
||||
var typeCondition = node.typeCondition;
|
||||
|
||||
if (typeCondition) {
|
||||
var type = typeFromAST(context.getSchema(), typeCondition);
|
||||
|
||||
if (type && !isCompositeType(type)) {
|
||||
context.reportError(new GraphQLError(inlineFragmentOnNonCompositeErrorMessage(print(typeCondition)), [typeCondition]));
|
||||
}
|
||||
}
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
var type = typeFromAST(context.getSchema(), node.typeCondition);
|
||||
|
||||
if (type && !isCompositeType(type)) {
|
||||
context.reportError(new GraphQLError(fragmentOnNonCompositeErrorMessage(node.name.value, print(node.typeCondition)), [node.typeCondition]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unknownArgMessage = unknownArgMessage;
|
||||
exports.unknownDirectiveArgMessage = unknownDirectiveArgMessage;
|
||||
exports.KnownArgumentNames = KnownArgumentNames;
|
||||
exports.KnownArgumentNamesOnDirectives = KnownArgumentNamesOnDirectives;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _suggestionList = _interopRequireDefault(require("../../jsutils/suggestionList"));
|
||||
|
||||
var _quotedOrList = _interopRequireDefault(require("../../jsutils/quotedOrList"));
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _directives = require("../../type/directives");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function unknownArgMessage(argName, fieldName, typeName, suggestedArgs) {
|
||||
var message = "Unknown argument \"".concat(argName, "\" on field \"").concat(fieldName, "\" of ") + "type \"".concat(typeName, "\".");
|
||||
|
||||
if (suggestedArgs.length) {
|
||||
message += " Did you mean ".concat((0, _quotedOrList.default)(suggestedArgs), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function unknownDirectiveArgMessage(argName, directiveName, suggestedArgs) {
|
||||
var message = "Unknown argument \"".concat(argName, "\" on directive \"@").concat(directiveName, "\".");
|
||||
|
||||
if (suggestedArgs.length) {
|
||||
message += " Did you mean ".concat((0, _quotedOrList.default)(suggestedArgs), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Known argument names
|
||||
*
|
||||
* A GraphQL field is only valid if all supplied arguments are defined by
|
||||
* that field.
|
||||
*/
|
||||
|
||||
|
||||
function KnownArgumentNames(context) {
|
||||
return _objectSpread({}, KnownArgumentNamesOnDirectives(context), {
|
||||
Argument: function Argument(argNode) {
|
||||
var argDef = context.getArgument();
|
||||
var fieldDef = context.getFieldDef();
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if (!argDef && fieldDef && parentType) {
|
||||
var argName = argNode.name.value;
|
||||
var knownArgsNames = fieldDef.args.map(function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownArgMessage(argName, fieldDef.name, parentType.name, (0, _suggestionList.default)(argName, knownArgsNames)), argNode));
|
||||
}
|
||||
}
|
||||
});
|
||||
} // @internal
|
||||
|
||||
|
||||
function KnownArgumentNamesOnDirectives(context) {
|
||||
var directiveArgs = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = definedDirectives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
directiveArgs[directive.name] = directive.args.map(function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = astDefinitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var def = _step2.value;
|
||||
|
||||
if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) {
|
||||
directiveArgs[def.name.value] = def.arguments ? def.arguments.map(function (arg) {
|
||||
return arg.name.value;
|
||||
}) : [];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: function Directive(directiveNode) {
|
||||
var directiveName = directiveNode.name.value;
|
||||
var knownArgs = directiveArgs[directiveName];
|
||||
|
||||
if (directiveNode.arguments && knownArgs) {
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = directiveNode.arguments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var argNode = _step3.value;
|
||||
var argName = argNode.name.value;
|
||||
|
||||
if (knownArgs.indexOf(argName) === -1) {
|
||||
var suggestions = (0, _suggestionList.default)(argName, knownArgs);
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownDirectiveArgMessage(argName, directiveName, suggestions), argNode));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type {
|
||||
ValidationContext,
|
||||
SDLValidationContext,
|
||||
} from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
|
||||
export function unknownArgMessage(
|
||||
argName: string,
|
||||
fieldName: string,
|
||||
typeName: string,
|
||||
suggestedArgs: Array<string>,
|
||||
): string {
|
||||
let message =
|
||||
`Unknown argument "${argName}" on field "${fieldName}" of ` +
|
||||
`type "${typeName}".`;
|
||||
if (suggestedArgs.length) {
|
||||
message += ` Did you mean ${quotedOrList(suggestedArgs)}?`;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
export function unknownDirectiveArgMessage(
|
||||
argName: string,
|
||||
directiveName: string,
|
||||
suggestedArgs: Array<string>,
|
||||
): string {
|
||||
let message = `Unknown argument "${argName}" on directive "@${directiveName}".`;
|
||||
if (suggestedArgs.length) {
|
||||
message += ` Did you mean ${quotedOrList(suggestedArgs)}?`;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Known argument names
|
||||
*
|
||||
* A GraphQL field is only valid if all supplied arguments are defined by
|
||||
* that field.
|
||||
*/
|
||||
export function KnownArgumentNames(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
...KnownArgumentNamesOnDirectives(context),
|
||||
Argument(argNode) {
|
||||
const argDef = context.getArgument();
|
||||
const fieldDef = context.getFieldDef();
|
||||
const parentType = context.getParentType();
|
||||
|
||||
if (!argDef && fieldDef && parentType) {
|
||||
const argName = argNode.name.value;
|
||||
const knownArgsNames = fieldDef.args.map(arg => arg.name);
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
unknownArgMessage(
|
||||
argName,
|
||||
fieldDef.name,
|
||||
parentType.name,
|
||||
suggestionList(argName, knownArgsNames),
|
||||
),
|
||||
argNode,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function KnownArgumentNamesOnDirectives(
|
||||
context: ValidationContext | SDLValidationContext,
|
||||
): ASTVisitor {
|
||||
const directiveArgs = Object.create(null);
|
||||
|
||||
const schema = context.getSchema();
|
||||
const definedDirectives = schema
|
||||
? schema.getDirectives()
|
||||
: specifiedDirectives;
|
||||
for (const directive of definedDirectives) {
|
||||
directiveArgs[directive.name] = directive.args.map(arg => arg.name);
|
||||
}
|
||||
|
||||
const astDefinitions = context.getDocument().definitions;
|
||||
for (const def of astDefinitions) {
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
directiveArgs[def.name.value] = def.arguments
|
||||
? def.arguments.map(arg => arg.name.value)
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive(directiveNode) {
|
||||
const directiveName = directiveNode.name.value;
|
||||
const knownArgs = directiveArgs[directiveName];
|
||||
|
||||
if (directiveNode.arguments && knownArgs) {
|
||||
for (const argNode of directiveNode.arguments) {
|
||||
const argName = argNode.name.value;
|
||||
if (knownArgs.indexOf(argName) === -1) {
|
||||
const suggestions = suggestionList(argName, knownArgs);
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
unknownDirectiveArgMessage(argName, directiveName, suggestions),
|
||||
argNode,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
export function unknownArgMessage(argName, fieldName, typeName, suggestedArgs) {
|
||||
var message = "Unknown argument \"".concat(argName, "\" on field \"").concat(fieldName, "\" of ") + "type \"".concat(typeName, "\".");
|
||||
|
||||
if (suggestedArgs.length) {
|
||||
message += " Did you mean ".concat(quotedOrList(suggestedArgs), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
export function unknownDirectiveArgMessage(argName, directiveName, suggestedArgs) {
|
||||
var message = "Unknown argument \"".concat(argName, "\" on directive \"@").concat(directiveName, "\".");
|
||||
|
||||
if (suggestedArgs.length) {
|
||||
message += " Did you mean ".concat(quotedOrList(suggestedArgs), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Known argument names
|
||||
*
|
||||
* A GraphQL field is only valid if all supplied arguments are defined by
|
||||
* that field.
|
||||
*/
|
||||
|
||||
export function KnownArgumentNames(context) {
|
||||
return _objectSpread({}, KnownArgumentNamesOnDirectives(context), {
|
||||
Argument: function Argument(argNode) {
|
||||
var argDef = context.getArgument();
|
||||
var fieldDef = context.getFieldDef();
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if (!argDef && fieldDef && parentType) {
|
||||
var argName = argNode.name.value;
|
||||
var knownArgsNames = fieldDef.args.map(function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
context.reportError(new GraphQLError(unknownArgMessage(argName, fieldDef.name, parentType.name, suggestionList(argName, knownArgsNames)), argNode));
|
||||
}
|
||||
}
|
||||
});
|
||||
} // @internal
|
||||
|
||||
export function KnownArgumentNamesOnDirectives(context) {
|
||||
var directiveArgs = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : specifiedDirectives;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = definedDirectives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
directiveArgs[directive.name] = directive.args.map(function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = astDefinitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var def = _step2.value;
|
||||
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
directiveArgs[def.name.value] = def.arguments ? def.arguments.map(function (arg) {
|
||||
return arg.name.value;
|
||||
}) : [];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: function Directive(directiveNode) {
|
||||
var directiveName = directiveNode.name.value;
|
||||
var knownArgs = directiveArgs[directiveName];
|
||||
|
||||
if (directiveNode.arguments && knownArgs) {
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = directiveNode.arguments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var argNode = _step3.value;
|
||||
var argName = argNode.name.value;
|
||||
|
||||
if (knownArgs.indexOf(argName) === -1) {
|
||||
var suggestions = suggestionList(argName, knownArgs);
|
||||
context.reportError(new GraphQLError(unknownDirectiveArgMessage(argName, directiveName, suggestions), argNode));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unknownDirectiveMessage = unknownDirectiveMessage;
|
||||
exports.misplacedDirectiveMessage = misplacedDirectiveMessage;
|
||||
exports.KnownDirectives = KnownDirectives;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _directiveLocation = require("../../language/directiveLocation");
|
||||
|
||||
var _directives = require("../../type/directives");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function unknownDirectiveMessage(directiveName) {
|
||||
return "Unknown directive \"".concat(directiveName, "\".");
|
||||
}
|
||||
|
||||
function misplacedDirectiveMessage(directiveName, location) {
|
||||
return "Directive \"".concat(directiveName, "\" may not be used on ").concat(location, ".");
|
||||
}
|
||||
/**
|
||||
* Known directives
|
||||
*
|
||||
* A GraphQL document is only valid if all `@directives` are known by the
|
||||
* schema and legally positioned.
|
||||
*/
|
||||
|
||||
|
||||
function KnownDirectives(context) {
|
||||
var locationsMap = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = definedDirectives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
locationsMap[directive.name] = directive.locations;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = astDefinitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var def = _step2.value;
|
||||
|
||||
if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) {
|
||||
locationsMap[def.name.value] = def.locations.map(function (name) {
|
||||
return name.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: function Directive(node, key, parent, path, ancestors) {
|
||||
var name = node.name.value;
|
||||
var locations = locationsMap[name];
|
||||
|
||||
if (!locations) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownDirectiveMessage(name), [node]));
|
||||
return;
|
||||
}
|
||||
|
||||
var candidateLocation = getDirectiveLocationForASTPath(ancestors);
|
||||
|
||||
if (candidateLocation && locations.indexOf(candidateLocation) === -1) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(misplacedDirectiveMessage(name, candidateLocation), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getDirectiveLocationForASTPath(ancestors) {
|
||||
var appliedTo = ancestors[ancestors.length - 1];
|
||||
|
||||
if (!Array.isArray(appliedTo)) {
|
||||
switch (appliedTo.kind) {
|
||||
case _kinds.Kind.OPERATION_DEFINITION:
|
||||
switch (appliedTo.operation) {
|
||||
case 'query':
|
||||
return _directiveLocation.DirectiveLocation.QUERY;
|
||||
|
||||
case 'mutation':
|
||||
return _directiveLocation.DirectiveLocation.MUTATION;
|
||||
|
||||
case 'subscription':
|
||||
return _directiveLocation.DirectiveLocation.SUBSCRIPTION;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case _kinds.Kind.FIELD:
|
||||
return _directiveLocation.DirectiveLocation.FIELD;
|
||||
|
||||
case _kinds.Kind.FRAGMENT_SPREAD:
|
||||
return _directiveLocation.DirectiveLocation.FRAGMENT_SPREAD;
|
||||
|
||||
case _kinds.Kind.INLINE_FRAGMENT:
|
||||
return _directiveLocation.DirectiveLocation.INLINE_FRAGMENT;
|
||||
|
||||
case _kinds.Kind.FRAGMENT_DEFINITION:
|
||||
return _directiveLocation.DirectiveLocation.FRAGMENT_DEFINITION;
|
||||
|
||||
case _kinds.Kind.VARIABLE_DEFINITION:
|
||||
return _directiveLocation.DirectiveLocation.VARIABLE_DEFINITION;
|
||||
|
||||
case _kinds.Kind.SCHEMA_DEFINITION:
|
||||
case _kinds.Kind.SCHEMA_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.SCHEMA;
|
||||
|
||||
case _kinds.Kind.SCALAR_TYPE_DEFINITION:
|
||||
case _kinds.Kind.SCALAR_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.SCALAR;
|
||||
|
||||
case _kinds.Kind.OBJECT_TYPE_DEFINITION:
|
||||
case _kinds.Kind.OBJECT_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.OBJECT;
|
||||
|
||||
case _kinds.Kind.FIELD_DEFINITION:
|
||||
return _directiveLocation.DirectiveLocation.FIELD_DEFINITION;
|
||||
|
||||
case _kinds.Kind.INTERFACE_TYPE_DEFINITION:
|
||||
case _kinds.Kind.INTERFACE_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.INTERFACE;
|
||||
|
||||
case _kinds.Kind.UNION_TYPE_DEFINITION:
|
||||
case _kinds.Kind.UNION_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.UNION;
|
||||
|
||||
case _kinds.Kind.ENUM_TYPE_DEFINITION:
|
||||
case _kinds.Kind.ENUM_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.ENUM;
|
||||
|
||||
case _kinds.Kind.ENUM_VALUE_DEFINITION:
|
||||
return _directiveLocation.DirectiveLocation.ENUM_VALUE;
|
||||
|
||||
case _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
||||
case _kinds.Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
||||
return _directiveLocation.DirectiveLocation.INPUT_OBJECT;
|
||||
|
||||
case _kinds.Kind.INPUT_VALUE_DEFINITION:
|
||||
var parentNode = ancestors[ancestors.length - 3];
|
||||
return parentNode.kind === _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION ? _directiveLocation.DirectiveLocation.INPUT_FIELD_DEFINITION : _directiveLocation.DirectiveLocation.ARGUMENT_DEFINITION;
|
||||
}
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type {
|
||||
ValidationContext,
|
||||
SDLValidationContext,
|
||||
} from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { DirectiveLocation } from '../../language/directiveLocation';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
|
||||
export function unknownDirectiveMessage(directiveName: string): string {
|
||||
return `Unknown directive "${directiveName}".`;
|
||||
}
|
||||
|
||||
export function misplacedDirectiveMessage(
|
||||
directiveName: string,
|
||||
location: string,
|
||||
): string {
|
||||
return `Directive "${directiveName}" may not be used on ${location}.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Known directives
|
||||
*
|
||||
* A GraphQL document is only valid if all `@directives` are known by the
|
||||
* schema and legally positioned.
|
||||
*/
|
||||
export function KnownDirectives(
|
||||
context: ValidationContext | SDLValidationContext,
|
||||
): ASTVisitor {
|
||||
const locationsMap = Object.create(null);
|
||||
|
||||
const schema = context.getSchema();
|
||||
const definedDirectives = schema
|
||||
? schema.getDirectives()
|
||||
: specifiedDirectives;
|
||||
for (const directive of definedDirectives) {
|
||||
locationsMap[directive.name] = directive.locations;
|
||||
}
|
||||
|
||||
const astDefinitions = context.getDocument().definitions;
|
||||
for (const def of astDefinitions) {
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
locationsMap[def.name.value] = def.locations.map(name => name.value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive(node, key, parent, path, ancestors) {
|
||||
const name = node.name.value;
|
||||
const locations = locationsMap[name];
|
||||
|
||||
if (!locations) {
|
||||
context.reportError(
|
||||
new GraphQLError(unknownDirectiveMessage(name), [node]),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const candidateLocation = getDirectiveLocationForASTPath(ancestors);
|
||||
if (candidateLocation && locations.indexOf(candidateLocation) === -1) {
|
||||
context.reportError(
|
||||
new GraphQLError(misplacedDirectiveMessage(name, candidateLocation), [
|
||||
node,
|
||||
]),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getDirectiveLocationForASTPath(ancestors) {
|
||||
const appliedTo = ancestors[ancestors.length - 1];
|
||||
if (!Array.isArray(appliedTo)) {
|
||||
switch (appliedTo.kind) {
|
||||
case Kind.OPERATION_DEFINITION:
|
||||
switch (appliedTo.operation) {
|
||||
case 'query':
|
||||
return DirectiveLocation.QUERY;
|
||||
case 'mutation':
|
||||
return DirectiveLocation.MUTATION;
|
||||
case 'subscription':
|
||||
return DirectiveLocation.SUBSCRIPTION;
|
||||
}
|
||||
break;
|
||||
case Kind.FIELD:
|
||||
return DirectiveLocation.FIELD;
|
||||
case Kind.FRAGMENT_SPREAD:
|
||||
return DirectiveLocation.FRAGMENT_SPREAD;
|
||||
case Kind.INLINE_FRAGMENT:
|
||||
return DirectiveLocation.INLINE_FRAGMENT;
|
||||
case Kind.FRAGMENT_DEFINITION:
|
||||
return DirectiveLocation.FRAGMENT_DEFINITION;
|
||||
case Kind.VARIABLE_DEFINITION:
|
||||
return DirectiveLocation.VARIABLE_DEFINITION;
|
||||
case Kind.SCHEMA_DEFINITION:
|
||||
case Kind.SCHEMA_EXTENSION:
|
||||
return DirectiveLocation.SCHEMA;
|
||||
case Kind.SCALAR_TYPE_DEFINITION:
|
||||
case Kind.SCALAR_TYPE_EXTENSION:
|
||||
return DirectiveLocation.SCALAR;
|
||||
case Kind.OBJECT_TYPE_DEFINITION:
|
||||
case Kind.OBJECT_TYPE_EXTENSION:
|
||||
return DirectiveLocation.OBJECT;
|
||||
case Kind.FIELD_DEFINITION:
|
||||
return DirectiveLocation.FIELD_DEFINITION;
|
||||
case Kind.INTERFACE_TYPE_DEFINITION:
|
||||
case Kind.INTERFACE_TYPE_EXTENSION:
|
||||
return DirectiveLocation.INTERFACE;
|
||||
case Kind.UNION_TYPE_DEFINITION:
|
||||
case Kind.UNION_TYPE_EXTENSION:
|
||||
return DirectiveLocation.UNION;
|
||||
case Kind.ENUM_TYPE_DEFINITION:
|
||||
case Kind.ENUM_TYPE_EXTENSION:
|
||||
return DirectiveLocation.ENUM;
|
||||
case Kind.ENUM_VALUE_DEFINITION:
|
||||
return DirectiveLocation.ENUM_VALUE;
|
||||
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
||||
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
||||
return DirectiveLocation.INPUT_OBJECT;
|
||||
case Kind.INPUT_VALUE_DEFINITION:
|
||||
const parentNode = ancestors[ancestors.length - 3];
|
||||
return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION
|
||||
? DirectiveLocation.INPUT_FIELD_DEFINITION
|
||||
: DirectiveLocation.ARGUMENT_DEFINITION;
|
||||
}
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { DirectiveLocation } from '../../language/directiveLocation';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
export function unknownDirectiveMessage(directiveName) {
|
||||
return "Unknown directive \"".concat(directiveName, "\".");
|
||||
}
|
||||
export function misplacedDirectiveMessage(directiveName, location) {
|
||||
return "Directive \"".concat(directiveName, "\" may not be used on ").concat(location, ".");
|
||||
}
|
||||
/**
|
||||
* Known directives
|
||||
*
|
||||
* A GraphQL document is only valid if all `@directives` are known by the
|
||||
* schema and legally positioned.
|
||||
*/
|
||||
|
||||
export function KnownDirectives(context) {
|
||||
var locationsMap = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : specifiedDirectives;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = definedDirectives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
locationsMap[directive.name] = directive.locations;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = astDefinitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var def = _step2.value;
|
||||
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
locationsMap[def.name.value] = def.locations.map(function (name) {
|
||||
return name.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: function Directive(node, key, parent, path, ancestors) {
|
||||
var name = node.name.value;
|
||||
var locations = locationsMap[name];
|
||||
|
||||
if (!locations) {
|
||||
context.reportError(new GraphQLError(unknownDirectiveMessage(name), [node]));
|
||||
return;
|
||||
}
|
||||
|
||||
var candidateLocation = getDirectiveLocationForASTPath(ancestors);
|
||||
|
||||
if (candidateLocation && locations.indexOf(candidateLocation) === -1) {
|
||||
context.reportError(new GraphQLError(misplacedDirectiveMessage(name, candidateLocation), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getDirectiveLocationForASTPath(ancestors) {
|
||||
var appliedTo = ancestors[ancestors.length - 1];
|
||||
|
||||
if (!Array.isArray(appliedTo)) {
|
||||
switch (appliedTo.kind) {
|
||||
case Kind.OPERATION_DEFINITION:
|
||||
switch (appliedTo.operation) {
|
||||
case 'query':
|
||||
return DirectiveLocation.QUERY;
|
||||
|
||||
case 'mutation':
|
||||
return DirectiveLocation.MUTATION;
|
||||
|
||||
case 'subscription':
|
||||
return DirectiveLocation.SUBSCRIPTION;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Kind.FIELD:
|
||||
return DirectiveLocation.FIELD;
|
||||
|
||||
case Kind.FRAGMENT_SPREAD:
|
||||
return DirectiveLocation.FRAGMENT_SPREAD;
|
||||
|
||||
case Kind.INLINE_FRAGMENT:
|
||||
return DirectiveLocation.INLINE_FRAGMENT;
|
||||
|
||||
case Kind.FRAGMENT_DEFINITION:
|
||||
return DirectiveLocation.FRAGMENT_DEFINITION;
|
||||
|
||||
case Kind.VARIABLE_DEFINITION:
|
||||
return DirectiveLocation.VARIABLE_DEFINITION;
|
||||
|
||||
case Kind.SCHEMA_DEFINITION:
|
||||
case Kind.SCHEMA_EXTENSION:
|
||||
return DirectiveLocation.SCHEMA;
|
||||
|
||||
case Kind.SCALAR_TYPE_DEFINITION:
|
||||
case Kind.SCALAR_TYPE_EXTENSION:
|
||||
return DirectiveLocation.SCALAR;
|
||||
|
||||
case Kind.OBJECT_TYPE_DEFINITION:
|
||||
case Kind.OBJECT_TYPE_EXTENSION:
|
||||
return DirectiveLocation.OBJECT;
|
||||
|
||||
case Kind.FIELD_DEFINITION:
|
||||
return DirectiveLocation.FIELD_DEFINITION;
|
||||
|
||||
case Kind.INTERFACE_TYPE_DEFINITION:
|
||||
case Kind.INTERFACE_TYPE_EXTENSION:
|
||||
return DirectiveLocation.INTERFACE;
|
||||
|
||||
case Kind.UNION_TYPE_DEFINITION:
|
||||
case Kind.UNION_TYPE_EXTENSION:
|
||||
return DirectiveLocation.UNION;
|
||||
|
||||
case Kind.ENUM_TYPE_DEFINITION:
|
||||
case Kind.ENUM_TYPE_EXTENSION:
|
||||
return DirectiveLocation.ENUM;
|
||||
|
||||
case Kind.ENUM_VALUE_DEFINITION:
|
||||
return DirectiveLocation.ENUM_VALUE;
|
||||
|
||||
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
||||
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
|
||||
return DirectiveLocation.INPUT_OBJECT;
|
||||
|
||||
case Kind.INPUT_VALUE_DEFINITION:
|
||||
var parentNode = ancestors[ancestors.length - 3];
|
||||
return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ? DirectiveLocation.INPUT_FIELD_DEFINITION : DirectiveLocation.ARGUMENT_DEFINITION;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unknownFragmentMessage = unknownFragmentMessage;
|
||||
exports.KnownFragmentNames = KnownFragmentNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function unknownFragmentMessage(fragName) {
|
||||
return "Unknown fragment \"".concat(fragName, "\".");
|
||||
}
|
||||
/**
|
||||
* Known fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all `...Fragment` fragment spreads refer
|
||||
* to fragments defined in the same document.
|
||||
*/
|
||||
|
||||
|
||||
function KnownFragmentNames(context) {
|
||||
return {
|
||||
FragmentSpread: function FragmentSpread(node) {
|
||||
var fragmentName = node.name.value;
|
||||
var fragment = context.getFragment(fragmentName);
|
||||
|
||||
if (!fragment) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownFragmentMessage(fragmentName), [node.name]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function unknownFragmentMessage(fragName: string): string {
|
||||
return `Unknown fragment "${fragName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Known fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all `...Fragment` fragment spreads refer
|
||||
* to fragments defined in the same document.
|
||||
*/
|
||||
export function KnownFragmentNames(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
FragmentSpread(node) {
|
||||
const fragmentName = node.name.value;
|
||||
const fragment = context.getFragment(fragmentName);
|
||||
if (!fragment) {
|
||||
context.reportError(
|
||||
new GraphQLError(unknownFragmentMessage(fragmentName), [node.name]),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function unknownFragmentMessage(fragName) {
|
||||
return "Unknown fragment \"".concat(fragName, "\".");
|
||||
}
|
||||
/**
|
||||
* Known fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all `...Fragment` fragment spreads refer
|
||||
* to fragments defined in the same document.
|
||||
*/
|
||||
|
||||
export function KnownFragmentNames(context) {
|
||||
return {
|
||||
FragmentSpread: function FragmentSpread(node) {
|
||||
var fragmentName = node.name.value;
|
||||
var fragment = context.getFragment(fragmentName);
|
||||
|
||||
if (!fragment) {
|
||||
context.reportError(new GraphQLError(unknownFragmentMessage(fragmentName), [node.name]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unknownTypeMessage = unknownTypeMessage;
|
||||
exports.KnownTypeNames = KnownTypeNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _suggestionList = _interopRequireDefault(require("../../jsutils/suggestionList"));
|
||||
|
||||
var _quotedOrList = _interopRequireDefault(require("../../jsutils/quotedOrList"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function unknownTypeMessage(typeName, suggestedTypes) {
|
||||
var message = "Unknown type \"".concat(typeName, "\".");
|
||||
|
||||
if (suggestedTypes.length) {
|
||||
message += " Did you mean ".concat((0, _quotedOrList.default)(suggestedTypes), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Known type names
|
||||
*
|
||||
* A GraphQL document is only valid if referenced types (specifically
|
||||
* variable definitions and fragment conditions) are defined by the type schema.
|
||||
*/
|
||||
|
||||
|
||||
function KnownTypeNames(context) {
|
||||
return {
|
||||
// TODO: when validating IDL, re-enable these. Experimental version does not
|
||||
// add unreferenced types, resulting in false-positive errors. Squelched
|
||||
// errors for now.
|
||||
ObjectTypeDefinition: function ObjectTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
InterfaceTypeDefinition: function InterfaceTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
UnionTypeDefinition: function UnionTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
InputObjectTypeDefinition: function InputObjectTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
NamedType: function NamedType(node) {
|
||||
var schema = context.getSchema();
|
||||
var typeName = node.name.value;
|
||||
var type = schema.getType(typeName);
|
||||
|
||||
if (!type) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownTypeMessage(typeName, (0, _suggestionList.default)(typeName, Object.keys(schema.getTypeMap()))), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function unknownTypeMessage(
|
||||
typeName: string,
|
||||
suggestedTypes: Array<string>,
|
||||
): string {
|
||||
let message = `Unknown type "${typeName}".`;
|
||||
if (suggestedTypes.length) {
|
||||
message += ` Did you mean ${quotedOrList(suggestedTypes)}?`;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Known type names
|
||||
*
|
||||
* A GraphQL document is only valid if referenced types (specifically
|
||||
* variable definitions and fragment conditions) are defined by the type schema.
|
||||
*/
|
||||
export function KnownTypeNames(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
// TODO: when validating IDL, re-enable these. Experimental version does not
|
||||
// add unreferenced types, resulting in false-positive errors. Squelched
|
||||
// errors for now.
|
||||
ObjectTypeDefinition: () => false,
|
||||
InterfaceTypeDefinition: () => false,
|
||||
UnionTypeDefinition: () => false,
|
||||
InputObjectTypeDefinition: () => false,
|
||||
NamedType(node) {
|
||||
const schema = context.getSchema();
|
||||
const typeName = node.name.value;
|
||||
const type = schema.getType(typeName);
|
||||
if (!type) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
unknownTypeMessage(
|
||||
typeName,
|
||||
suggestionList(typeName, Object.keys(schema.getTypeMap())),
|
||||
),
|
||||
[node],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
import quotedOrList from '../../jsutils/quotedOrList';
|
||||
export function unknownTypeMessage(typeName, suggestedTypes) {
|
||||
var message = "Unknown type \"".concat(typeName, "\".");
|
||||
|
||||
if (suggestedTypes.length) {
|
||||
message += " Did you mean ".concat(quotedOrList(suggestedTypes), "?");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Known type names
|
||||
*
|
||||
* A GraphQL document is only valid if referenced types (specifically
|
||||
* variable definitions and fragment conditions) are defined by the type schema.
|
||||
*/
|
||||
|
||||
export function KnownTypeNames(context) {
|
||||
return {
|
||||
// TODO: when validating IDL, re-enable these. Experimental version does not
|
||||
// add unreferenced types, resulting in false-positive errors. Squelched
|
||||
// errors for now.
|
||||
ObjectTypeDefinition: function ObjectTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
InterfaceTypeDefinition: function InterfaceTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
UnionTypeDefinition: function UnionTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
InputObjectTypeDefinition: function InputObjectTypeDefinition() {
|
||||
return false;
|
||||
},
|
||||
NamedType: function NamedType(node) {
|
||||
var schema = context.getSchema();
|
||||
var typeName = node.name.value;
|
||||
var type = schema.getType(typeName);
|
||||
|
||||
if (!type) {
|
||||
context.reportError(new GraphQLError(unknownTypeMessage(typeName, suggestionList(typeName, Object.keys(schema.getTypeMap()))), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.anonOperationNotAloneMessage = anonOperationNotAloneMessage;
|
||||
exports.LoneAnonymousOperation = LoneAnonymousOperation;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function anonOperationNotAloneMessage() {
|
||||
return 'This anonymous operation must be the only defined operation.';
|
||||
}
|
||||
/**
|
||||
* Lone anonymous operation
|
||||
*
|
||||
* A GraphQL document is only valid if when it contains an anonymous operation
|
||||
* (the query short-hand) that it contains only that one operation definition.
|
||||
*/
|
||||
|
||||
|
||||
function LoneAnonymousOperation(context) {
|
||||
var operationCount = 0;
|
||||
return {
|
||||
Document: function Document(node) {
|
||||
operationCount = node.definitions.filter(function (definition) {
|
||||
return definition.kind === _kinds.Kind.OPERATION_DEFINITION;
|
||||
}).length;
|
||||
},
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
if (!node.name && operationCount > 1) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(anonOperationNotAloneMessage(), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function anonOperationNotAloneMessage(): string {
|
||||
return 'This anonymous operation must be the only defined operation.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lone anonymous operation
|
||||
*
|
||||
* A GraphQL document is only valid if when it contains an anonymous operation
|
||||
* (the query short-hand) that it contains only that one operation definition.
|
||||
*/
|
||||
export function LoneAnonymousOperation(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
let operationCount = 0;
|
||||
return {
|
||||
Document(node) {
|
||||
operationCount = node.definitions.filter(
|
||||
definition => definition.kind === Kind.OPERATION_DEFINITION,
|
||||
).length;
|
||||
},
|
||||
OperationDefinition(node) {
|
||||
if (!node.name && operationCount > 1) {
|
||||
context.reportError(
|
||||
new GraphQLError(anonOperationNotAloneMessage(), [node]),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
export function anonOperationNotAloneMessage() {
|
||||
return 'This anonymous operation must be the only defined operation.';
|
||||
}
|
||||
/**
|
||||
* Lone anonymous operation
|
||||
*
|
||||
* A GraphQL document is only valid if when it contains an anonymous operation
|
||||
* (the query short-hand) that it contains only that one operation definition.
|
||||
*/
|
||||
|
||||
export function LoneAnonymousOperation(context) {
|
||||
var operationCount = 0;
|
||||
return {
|
||||
Document: function Document(node) {
|
||||
operationCount = node.definitions.filter(function (definition) {
|
||||
return definition.kind === Kind.OPERATION_DEFINITION;
|
||||
}).length;
|
||||
},
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
if (!node.name && operationCount > 1) {
|
||||
context.reportError(new GraphQLError(anonOperationNotAloneMessage(), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.schemaDefinitionNotAloneMessage = schemaDefinitionNotAloneMessage;
|
||||
exports.canNotDefineSchemaWithinExtensionMessage = canNotDefineSchemaWithinExtensionMessage;
|
||||
exports.LoneSchemaDefinition = LoneSchemaDefinition;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function schemaDefinitionNotAloneMessage() {
|
||||
return 'Must provide only one schema definition.';
|
||||
}
|
||||
|
||||
function canNotDefineSchemaWithinExtensionMessage() {
|
||||
return 'Cannot define a new schema within a schema extension.';
|
||||
}
|
||||
/**
|
||||
* Lone Schema definition
|
||||
*
|
||||
* A GraphQL document is only valid if it contains only one schema definition.
|
||||
*/
|
||||
|
||||
|
||||
function LoneSchemaDefinition(context) {
|
||||
var oldSchema = context.getSchema();
|
||||
var alreadyDefined = oldSchema && (oldSchema.astNode || oldSchema.getQueryType() || oldSchema.getMutationType() || oldSchema.getSubscriptionType());
|
||||
var schemaDefinitionsCount = 0;
|
||||
return {
|
||||
SchemaDefinition: function SchemaDefinition(node) {
|
||||
if (alreadyDefined) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(canNotDefineSchemaWithinExtensionMessage(), node));
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaDefinitionsCount > 0) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(schemaDefinitionNotAloneMessage(), node));
|
||||
}
|
||||
|
||||
++schemaDefinitionsCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2018-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { SDLValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function schemaDefinitionNotAloneMessage(): string {
|
||||
return 'Must provide only one schema definition.';
|
||||
}
|
||||
|
||||
export function canNotDefineSchemaWithinExtensionMessage(): string {
|
||||
return 'Cannot define a new schema within a schema extension.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lone Schema definition
|
||||
*
|
||||
* A GraphQL document is only valid if it contains only one schema definition.
|
||||
*/
|
||||
export function LoneSchemaDefinition(
|
||||
context: SDLValidationContext,
|
||||
): ASTVisitor {
|
||||
const oldSchema = context.getSchema();
|
||||
const alreadyDefined =
|
||||
oldSchema &&
|
||||
(oldSchema.astNode ||
|
||||
oldSchema.getQueryType() ||
|
||||
oldSchema.getMutationType() ||
|
||||
oldSchema.getSubscriptionType());
|
||||
|
||||
let schemaDefinitionsCount = 0;
|
||||
return {
|
||||
SchemaDefinition(node) {
|
||||
if (alreadyDefined) {
|
||||
context.reportError(
|
||||
new GraphQLError(canNotDefineSchemaWithinExtensionMessage(), node),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaDefinitionsCount > 0) {
|
||||
context.reportError(
|
||||
new GraphQLError(schemaDefinitionNotAloneMessage(), node),
|
||||
);
|
||||
}
|
||||
++schemaDefinitionsCount;
|
||||
},
|
||||
};
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2018-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function schemaDefinitionNotAloneMessage() {
|
||||
return 'Must provide only one schema definition.';
|
||||
}
|
||||
export function canNotDefineSchemaWithinExtensionMessage() {
|
||||
return 'Cannot define a new schema within a schema extension.';
|
||||
}
|
||||
/**
|
||||
* Lone Schema definition
|
||||
*
|
||||
* A GraphQL document is only valid if it contains only one schema definition.
|
||||
*/
|
||||
|
||||
export function LoneSchemaDefinition(context) {
|
||||
var oldSchema = context.getSchema();
|
||||
var alreadyDefined = oldSchema && (oldSchema.astNode || oldSchema.getQueryType() || oldSchema.getMutationType() || oldSchema.getSubscriptionType());
|
||||
var schemaDefinitionsCount = 0;
|
||||
return {
|
||||
SchemaDefinition: function SchemaDefinition(node) {
|
||||
if (alreadyDefined) {
|
||||
context.reportError(new GraphQLError(canNotDefineSchemaWithinExtensionMessage(), node));
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaDefinitionsCount > 0) {
|
||||
context.reportError(new GraphQLError(schemaDefinitionNotAloneMessage(), node));
|
||||
}
|
||||
|
||||
++schemaDefinitionsCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.cycleErrorMessage = cycleErrorMessage;
|
||||
exports.NoFragmentCycles = NoFragmentCycles;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function cycleErrorMessage(fragName, spreadNames) {
|
||||
var via = spreadNames.length ? ' via ' + spreadNames.join(', ') : '';
|
||||
return "Cannot spread fragment \"".concat(fragName, "\" within itself").concat(via, ".");
|
||||
}
|
||||
|
||||
function NoFragmentCycles(context) {
|
||||
// Tracks already visited fragments to maintain O(N) and to ensure that cycles
|
||||
// are not redundantly reported.
|
||||
var visitedFrags = Object.create(null); // Array of AST nodes used to produce meaningful errors
|
||||
|
||||
var spreadPath = []; // Position in the spread path
|
||||
|
||||
var spreadPathIndexByName = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
detectCycleRecursive(node);
|
||||
return false;
|
||||
}
|
||||
}; // This does a straight-forward DFS to find cycles.
|
||||
// It does not terminate when a cycle was found but continues to explore
|
||||
// the graph to find all possible cycles.
|
||||
|
||||
function detectCycleRecursive(fragment) {
|
||||
if (visitedFrags[fragment.name.value]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentName = fragment.name.value;
|
||||
visitedFrags[fragmentName] = true;
|
||||
var spreadNodes = context.getFragmentSpreads(fragment.selectionSet);
|
||||
|
||||
if (spreadNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = spreadPath.length;
|
||||
|
||||
for (var i = 0; i < spreadNodes.length; i++) {
|
||||
var spreadNode = spreadNodes[i];
|
||||
var spreadName = spreadNode.name.value;
|
||||
var cycleIndex = spreadPathIndexByName[spreadName];
|
||||
spreadPath.push(spreadNode);
|
||||
|
||||
if (cycleIndex === undefined) {
|
||||
var spreadFragment = context.getFragment(spreadName);
|
||||
|
||||
if (spreadFragment) {
|
||||
detectCycleRecursive(spreadFragment);
|
||||
}
|
||||
} else {
|
||||
var cyclePath = spreadPath.slice(cycleIndex);
|
||||
var fragmentNames = cyclePath.slice(0, -1).map(function (s) {
|
||||
return s.name.value;
|
||||
});
|
||||
context.reportError(new _GraphQLError.GraphQLError(cycleErrorMessage(spreadName, fragmentNames), cyclePath));
|
||||
}
|
||||
|
||||
spreadPath.pop();
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = undefined;
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { FragmentDefinitionNode } from '../../language/ast';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function cycleErrorMessage(
|
||||
fragName: string,
|
||||
spreadNames: Array<string>,
|
||||
): string {
|
||||
const via = spreadNames.length ? ' via ' + spreadNames.join(', ') : '';
|
||||
return `Cannot spread fragment "${fragName}" within itself${via}.`;
|
||||
}
|
||||
|
||||
export function NoFragmentCycles(context: ValidationContext): ASTVisitor {
|
||||
// Tracks already visited fragments to maintain O(N) and to ensure that cycles
|
||||
// are not redundantly reported.
|
||||
const visitedFrags = Object.create(null);
|
||||
|
||||
// Array of AST nodes used to produce meaningful errors
|
||||
const spreadPath = [];
|
||||
|
||||
// Position in the spread path
|
||||
const spreadPathIndexByName = Object.create(null);
|
||||
|
||||
return {
|
||||
OperationDefinition: () => false,
|
||||
FragmentDefinition(node) {
|
||||
detectCycleRecursive(node);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
// This does a straight-forward DFS to find cycles.
|
||||
// It does not terminate when a cycle was found but continues to explore
|
||||
// the graph to find all possible cycles.
|
||||
function detectCycleRecursive(fragment: FragmentDefinitionNode) {
|
||||
if (visitedFrags[fragment.name.value]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fragmentName = fragment.name.value;
|
||||
visitedFrags[fragmentName] = true;
|
||||
|
||||
const spreadNodes = context.getFragmentSpreads(fragment.selectionSet);
|
||||
if (spreadNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = spreadPath.length;
|
||||
|
||||
for (let i = 0; i < spreadNodes.length; i++) {
|
||||
const spreadNode = spreadNodes[i];
|
||||
const spreadName = spreadNode.name.value;
|
||||
const cycleIndex = spreadPathIndexByName[spreadName];
|
||||
|
||||
spreadPath.push(spreadNode);
|
||||
if (cycleIndex === undefined) {
|
||||
const spreadFragment = context.getFragment(spreadName);
|
||||
if (spreadFragment) {
|
||||
detectCycleRecursive(spreadFragment);
|
||||
}
|
||||
} else {
|
||||
const cyclePath = spreadPath.slice(cycleIndex);
|
||||
const fragmentNames = cyclePath.slice(0, -1).map(s => s.name.value);
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
cycleErrorMessage(spreadName, fragmentNames),
|
||||
cyclePath,
|
||||
),
|
||||
);
|
||||
}
|
||||
spreadPath.pop();
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = undefined;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function cycleErrorMessage(fragName, spreadNames) {
|
||||
var via = spreadNames.length ? ' via ' + spreadNames.join(', ') : '';
|
||||
return "Cannot spread fragment \"".concat(fragName, "\" within itself").concat(via, ".");
|
||||
}
|
||||
export function NoFragmentCycles(context) {
|
||||
// Tracks already visited fragments to maintain O(N) and to ensure that cycles
|
||||
// are not redundantly reported.
|
||||
var visitedFrags = Object.create(null); // Array of AST nodes used to produce meaningful errors
|
||||
|
||||
var spreadPath = []; // Position in the spread path
|
||||
|
||||
var spreadPathIndexByName = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
detectCycleRecursive(node);
|
||||
return false;
|
||||
}
|
||||
}; // This does a straight-forward DFS to find cycles.
|
||||
// It does not terminate when a cycle was found but continues to explore
|
||||
// the graph to find all possible cycles.
|
||||
|
||||
function detectCycleRecursive(fragment) {
|
||||
if (visitedFrags[fragment.name.value]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fragmentName = fragment.name.value;
|
||||
visitedFrags[fragmentName] = true;
|
||||
var spreadNodes = context.getFragmentSpreads(fragment.selectionSet);
|
||||
|
||||
if (spreadNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = spreadPath.length;
|
||||
|
||||
for (var i = 0; i < spreadNodes.length; i++) {
|
||||
var spreadNode = spreadNodes[i];
|
||||
var spreadName = spreadNode.name.value;
|
||||
var cycleIndex = spreadPathIndexByName[spreadName];
|
||||
spreadPath.push(spreadNode);
|
||||
|
||||
if (cycleIndex === undefined) {
|
||||
var spreadFragment = context.getFragment(spreadName);
|
||||
|
||||
if (spreadFragment) {
|
||||
detectCycleRecursive(spreadFragment);
|
||||
}
|
||||
} else {
|
||||
var cyclePath = spreadPath.slice(cycleIndex);
|
||||
var fragmentNames = cyclePath.slice(0, -1).map(function (s) {
|
||||
return s.name.value;
|
||||
});
|
||||
context.reportError(new GraphQLError(cycleErrorMessage(spreadName, fragmentNames), cyclePath));
|
||||
}
|
||||
|
||||
spreadPath.pop();
|
||||
}
|
||||
|
||||
spreadPathIndexByName[fragmentName] = undefined;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.undefinedVarMessage = undefinedVarMessage;
|
||||
exports.NoUndefinedVariables = NoUndefinedVariables;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function undefinedVarMessage(varName, opName) {
|
||||
return opName ? "Variable \"$".concat(varName, "\" is not defined by operation \"").concat(opName, "\".") : "Variable \"$".concat(varName, "\" is not defined.");
|
||||
}
|
||||
/**
|
||||
* No undefined variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables encountered, both directly
|
||||
* and via fragment spreads, are defined by that operation.
|
||||
*/
|
||||
|
||||
|
||||
function NoUndefinedVariables(context) {
|
||||
var variableNameDefined = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
variableNameDefined = Object.create(null);
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node;
|
||||
var varName = node.name.value;
|
||||
|
||||
if (variableNameDefined[varName] !== true) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(undefinedVarMessage(varName, operation.name && operation.name.value), [node, operation]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
variableNameDefined[node.variable.name.value] = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function undefinedVarMessage(varName: string, opName: ?string): string {
|
||||
return opName
|
||||
? `Variable "$${varName}" is not defined by operation "${opName}".`
|
||||
: `Variable "$${varName}" is not defined.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* No undefined variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables encountered, both directly
|
||||
* and via fragment spreads, are defined by that operation.
|
||||
*/
|
||||
export function NoUndefinedVariables(context: ValidationContext): ASTVisitor {
|
||||
let variableNameDefined = Object.create(null);
|
||||
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter() {
|
||||
variableNameDefined = Object.create(null);
|
||||
},
|
||||
leave(operation) {
|
||||
const usages = context.getRecursiveVariableUsages(operation);
|
||||
|
||||
for (const { node } of usages) {
|
||||
const varName = node.name.value;
|
||||
if (variableNameDefined[varName] !== true) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
undefinedVarMessage(
|
||||
varName,
|
||||
operation.name && operation.name.value,
|
||||
),
|
||||
[node, operation],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
VariableDefinition(node) {
|
||||
variableNameDefined[node.variable.name.value] = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function undefinedVarMessage(varName, opName) {
|
||||
return opName ? "Variable \"$".concat(varName, "\" is not defined by operation \"").concat(opName, "\".") : "Variable \"$".concat(varName, "\" is not defined.");
|
||||
}
|
||||
/**
|
||||
* No undefined variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables encountered, both directly
|
||||
* and via fragment spreads, are defined by that operation.
|
||||
*/
|
||||
|
||||
export function NoUndefinedVariables(context) {
|
||||
var variableNameDefined = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
variableNameDefined = Object.create(null);
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node;
|
||||
var varName = node.name.value;
|
||||
|
||||
if (variableNameDefined[varName] !== true) {
|
||||
context.reportError(new GraphQLError(undefinedVarMessage(varName, operation.name && operation.name.value), [node, operation]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
variableNameDefined[node.variable.name.value] = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unusedFragMessage = unusedFragMessage;
|
||||
exports.NoUnusedFragments = NoUnusedFragments;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function unusedFragMessage(fragName) {
|
||||
return "Fragment \"".concat(fragName, "\" is never used.");
|
||||
}
|
||||
/**
|
||||
* No unused fragments
|
||||
*
|
||||
* A GraphQL document is only valid if all fragment definitions are spread
|
||||
* within operations, or spread within other fragments spread within operations.
|
||||
*/
|
||||
|
||||
|
||||
function NoUnusedFragments(context) {
|
||||
var operationDefs = [];
|
||||
var fragmentDefs = [];
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
operationDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
fragmentDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
Document: {
|
||||
leave: function leave() {
|
||||
var fragmentNameUsed = Object.create(null);
|
||||
|
||||
for (var _i = 0; _i < operationDefs.length; _i++) {
|
||||
var operation = operationDefs[_i];
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = context.getRecursivelyReferencedFragments(operation)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var fragment = _step.value;
|
||||
fragmentNameUsed[fragment.name.value] = true;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var _i2 = 0; _i2 < fragmentDefs.length; _i2++) {
|
||||
var fragmentDef = fragmentDefs[_i2];
|
||||
var fragName = fragmentDef.name.value;
|
||||
|
||||
if (fragmentNameUsed[fragName] !== true) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(unusedFragMessage(fragName), [fragmentDef]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function unusedFragMessage(fragName: string): string {
|
||||
return `Fragment "${fragName}" is never used.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* No unused fragments
|
||||
*
|
||||
* A GraphQL document is only valid if all fragment definitions are spread
|
||||
* within operations, or spread within other fragments spread within operations.
|
||||
*/
|
||||
export function NoUnusedFragments(context: ValidationContext): ASTVisitor {
|
||||
const operationDefs = [];
|
||||
const fragmentDefs = [];
|
||||
|
||||
return {
|
||||
OperationDefinition(node) {
|
||||
operationDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition(node) {
|
||||
fragmentDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
Document: {
|
||||
leave() {
|
||||
const fragmentNameUsed = Object.create(null);
|
||||
for (const operation of operationDefs) {
|
||||
for (const fragment of context.getRecursivelyReferencedFragments(
|
||||
operation,
|
||||
)) {
|
||||
fragmentNameUsed[fragment.name.value] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const fragmentDef of fragmentDefs) {
|
||||
const fragName = fragmentDef.name.value;
|
||||
if (fragmentNameUsed[fragName] !== true) {
|
||||
context.reportError(
|
||||
new GraphQLError(unusedFragMessage(fragName), [fragmentDef]),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function unusedFragMessage(fragName) {
|
||||
return "Fragment \"".concat(fragName, "\" is never used.");
|
||||
}
|
||||
/**
|
||||
* No unused fragments
|
||||
*
|
||||
* A GraphQL document is only valid if all fragment definitions are spread
|
||||
* within operations, or spread within other fragments spread within operations.
|
||||
*/
|
||||
|
||||
export function NoUnusedFragments(context) {
|
||||
var operationDefs = [];
|
||||
var fragmentDefs = [];
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
operationDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
fragmentDefs.push(node);
|
||||
return false;
|
||||
},
|
||||
Document: {
|
||||
leave: function leave() {
|
||||
var fragmentNameUsed = Object.create(null);
|
||||
|
||||
for (var _i = 0; _i < operationDefs.length; _i++) {
|
||||
var operation = operationDefs[_i];
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = context.getRecursivelyReferencedFragments(operation)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var fragment = _step.value;
|
||||
fragmentNameUsed[fragment.name.value] = true;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var _i2 = 0; _i2 < fragmentDefs.length; _i2++) {
|
||||
var fragmentDef = fragmentDefs[_i2];
|
||||
var fragName = fragmentDef.name.value;
|
||||
|
||||
if (fragmentNameUsed[fragName] !== true) {
|
||||
context.reportError(new GraphQLError(unusedFragMessage(fragName), [fragmentDef]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.unusedVariableMessage = unusedVariableMessage;
|
||||
exports.NoUnusedVariables = NoUnusedVariables;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function unusedVariableMessage(varName, opName) {
|
||||
return opName ? "Variable \"$".concat(varName, "\" is never used in operation \"").concat(opName, "\".") : "Variable \"$".concat(varName, "\" is never used.");
|
||||
}
|
||||
/**
|
||||
* No unused variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables defined by an operation
|
||||
* are used, either directly or within a spread fragment.
|
||||
*/
|
||||
|
||||
|
||||
function NoUnusedVariables(context) {
|
||||
var variableDefs = [];
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
variableDefs = [];
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var variableNameUsed = Object.create(null);
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var opName = operation.name ? operation.name.value : null;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node;
|
||||
variableNameUsed[node.name.value] = true;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var _i = 0; _i < variableDefs.length; _i++) {
|
||||
var variableDef = variableDefs[_i];
|
||||
var variableName = variableDef.variable.name.value;
|
||||
|
||||
if (variableNameUsed[variableName] !== true) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(unusedVariableMessage(variableName, opName), [variableDef]));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(def) {
|
||||
variableDefs.push(def);
|
||||
}
|
||||
};
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function unusedVariableMessage(
|
||||
varName: string,
|
||||
opName: ?string,
|
||||
): string {
|
||||
return opName
|
||||
? `Variable "$${varName}" is never used in operation "${opName}".`
|
||||
: `Variable "$${varName}" is never used.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* No unused variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables defined by an operation
|
||||
* are used, either directly or within a spread fragment.
|
||||
*/
|
||||
export function NoUnusedVariables(context: ValidationContext): ASTVisitor {
|
||||
let variableDefs = [];
|
||||
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter() {
|
||||
variableDefs = [];
|
||||
},
|
||||
leave(operation) {
|
||||
const variableNameUsed = Object.create(null);
|
||||
const usages = context.getRecursiveVariableUsages(operation);
|
||||
const opName = operation.name ? operation.name.value : null;
|
||||
|
||||
for (const { node } of usages) {
|
||||
variableNameUsed[node.name.value] = true;
|
||||
}
|
||||
|
||||
for (const variableDef of variableDefs) {
|
||||
const variableName = variableDef.variable.name.value;
|
||||
if (variableNameUsed[variableName] !== true) {
|
||||
context.reportError(
|
||||
new GraphQLError(unusedVariableMessage(variableName, opName), [
|
||||
variableDef,
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
VariableDefinition(def) {
|
||||
variableDefs.push(def);
|
||||
},
|
||||
};
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function unusedVariableMessage(varName, opName) {
|
||||
return opName ? "Variable \"$".concat(varName, "\" is never used in operation \"").concat(opName, "\".") : "Variable \"$".concat(varName, "\" is never used.");
|
||||
}
|
||||
/**
|
||||
* No unused variables
|
||||
*
|
||||
* A GraphQL operation is only valid if all variables defined by an operation
|
||||
* are used, either directly or within a spread fragment.
|
||||
*/
|
||||
|
||||
export function NoUnusedVariables(context) {
|
||||
var variableDefs = [];
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
variableDefs = [];
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var variableNameUsed = Object.create(null);
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var opName = operation.name ? operation.name.value : null;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node;
|
||||
variableNameUsed[node.name.value] = true;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var _i = 0; _i < variableDefs.length; _i++) {
|
||||
var variableDef = variableDefs[_i];
|
||||
var variableName = variableDef.variable.name.value;
|
||||
|
||||
if (variableNameUsed[variableName] !== true) {
|
||||
context.reportError(new GraphQLError(unusedVariableMessage(variableName, opName), [variableDef]));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(def) {
|
||||
variableDefs.push(def);
|
||||
}
|
||||
};
|
||||
}
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.fieldsConflictMessage = fieldsConflictMessage;
|
||||
exports.OverlappingFieldsCanBeMerged = OverlappingFieldsCanBeMerged;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _find = _interopRequireDefault(require("../../jsutils/find"));
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _printer = require("../../language/printer");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _typeFromAST = require("../../utilities/typeFromAST");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function fieldsConflictMessage(responseName, reason) {
|
||||
return "Fields \"".concat(responseName, "\" conflict because ").concat(reasonMessage(reason)) + '. Use different aliases on the fields to fetch both if this was ' + 'intentional.';
|
||||
}
|
||||
|
||||
function reasonMessage(reason) {
|
||||
if (Array.isArray(reason)) {
|
||||
return reason.map(function (_ref) {
|
||||
var responseName = _ref[0],
|
||||
subreason = _ref[1];
|
||||
return "subfields \"".concat(responseName, "\" conflict because ").concat(reasonMessage(subreason));
|
||||
}).join(' and ');
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
/**
|
||||
* Overlapping fields can be merged
|
||||
*
|
||||
* A selection set is only valid if all fields (including spreading any
|
||||
* fragments) either correspond to distinct response names or can be merged
|
||||
* without ambiguity.
|
||||
*/
|
||||
|
||||
|
||||
function OverlappingFieldsCanBeMerged(context) {
|
||||
// A memoization for when two fragments are compared "between" each other for
|
||||
// conflicts. Two fragments may be compared many times, so memoizing this can
|
||||
// dramatically improve the performance of this validator.
|
||||
var comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given
|
||||
// selection set. Selection sets may be asked for this information multiple
|
||||
// times, so this improves the performance of this validator.
|
||||
|
||||
var cachedFieldsAndFragmentNames = new Map();
|
||||
return {
|
||||
SelectionSet: function SelectionSet(selectionSet) {
|
||||
var conflicts = findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, context.getParentType(), selectionSet);
|
||||
|
||||
for (var _i = 0; _i < conflicts.length; _i++) {
|
||||
var _ref3 = conflicts[_i];
|
||||
var _ref2$ = _ref3[0];
|
||||
var responseName = _ref2$[0];
|
||||
var reason = _ref2$[1];
|
||||
var fields1 = _ref3[1];
|
||||
var fields2 = _ref3[2];
|
||||
context.reportError(new _GraphQLError.GraphQLError(fieldsConflictMessage(responseName, reason), fields1.concat(fields2)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm:
|
||||
*
|
||||
* Conflicts occur when two fields exist in a query which will produce the same
|
||||
* response name, but represent differing values, thus creating a conflict.
|
||||
* The algorithm below finds all conflicts via making a series of comparisons
|
||||
* between fields. In order to compare as few fields as possible, this makes
|
||||
* a series of comparisons "within" sets of fields and "between" sets of fields.
|
||||
*
|
||||
* Given any selection set, a collection produces both a set of fields by
|
||||
* also including all inline fragments, as well as a list of fragments
|
||||
* referenced by fragment spreads.
|
||||
*
|
||||
* A) Each selection set represented in the document first compares "within" its
|
||||
* collected set of fields, finding any conflicts between every pair of
|
||||
* overlapping fields.
|
||||
* Note: This is the *only time* that a the fields "within" a set are compared
|
||||
* to each other. After this only fields "between" sets are compared.
|
||||
*
|
||||
* B) Also, if any fragment is referenced in a selection set, then a
|
||||
* comparison is made "between" the original set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* C) Also, if multiple fragments are referenced, then comparisons
|
||||
* are made "between" each referenced fragment.
|
||||
*
|
||||
* D) When comparing "between" a set of fields and a referenced fragment, first
|
||||
* a comparison is made between each field in the original set of fields and
|
||||
* each field in the the referenced set of fields.
|
||||
*
|
||||
* E) Also, if any fragment is referenced in the referenced selection set,
|
||||
* then a comparison is made "between" the original set of fields and the
|
||||
* referenced fragment (recursively referring to step D).
|
||||
*
|
||||
* F) When comparing "between" two fragments, first a comparison is made between
|
||||
* each field in the first referenced set of fields and each field in the the
|
||||
* second referenced set of fields.
|
||||
*
|
||||
* G) Also, any fragments referenced by the first must be compared to the
|
||||
* second, and any fragments referenced by the second must be compared to the
|
||||
* first (recursively referring to step F).
|
||||
*
|
||||
* H) When comparing two fields, if both have selection sets, then a comparison
|
||||
* is made "between" both selection sets, first comparing the set of fields in
|
||||
* the first selection set with the set of fields in the second.
|
||||
*
|
||||
* I) Also, if any fragment is referenced in either selection set, then a
|
||||
* comparison is made "between" the other set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* J) Also, if two fragments are referenced in both selection sets, then a
|
||||
* comparison is made "between" the two fragments.
|
||||
*
|
||||
*/
|
||||
// Find all conflicts found "within" a selection set, including those found
|
||||
// via spreading in fragments. Called when visiting each SelectionSet in the
|
||||
// GraphQL Document.
|
||||
function findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentType, selectionSet) {
|
||||
var conflicts = [];
|
||||
|
||||
var _getFieldsAndFragment = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet),
|
||||
fieldMap = _getFieldsAndFragment[0],
|
||||
fragmentNames = _getFieldsAndFragment[1]; // (A) Find find all conflicts "within" the fields of this selection set.
|
||||
// Note: this is the *only place* `collectConflictsWithin` is called.
|
||||
|
||||
|
||||
collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap);
|
||||
|
||||
if (fragmentNames.length !== 0) {
|
||||
// (B) Then collect conflicts between these fields and those represented by
|
||||
// each spread fragment name found.
|
||||
var comparedFragments = Object.create(null);
|
||||
|
||||
for (var i = 0; i < fragmentNames.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); // (C) Then compare this fragment with all other fragments found in this
|
||||
// selection set to collect conflicts between fragments spread together.
|
||||
// This compares each item in the list of fragment names to every other
|
||||
// item in that same list (except for itself).
|
||||
|
||||
for (var j = i + 1; j < fragmentNames.length; j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fragmentNames[i], fragmentNames[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
} // Collect all conflicts found between a set of fields and a fragment reference
|
||||
// including via spreading in any nested fragments.
|
||||
|
||||
|
||||
function collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentName) {
|
||||
// Memoize so a fragment is not compared for conflicts more than once.
|
||||
if (comparedFragments[fragmentName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
comparedFragments[fragmentName] = true;
|
||||
var fragment = context.getFragment(fragmentName);
|
||||
|
||||
if (!fragment) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _getReferencedFieldsA = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment),
|
||||
fieldMap2 = _getReferencedFieldsA[0],
|
||||
fragmentNames2 = _getReferencedFieldsA[1]; // Do not compare a fragment's fieldMap to itself.
|
||||
|
||||
|
||||
if (fieldMap === fieldMap2) {
|
||||
return;
|
||||
} // (D) First collect any conflicts between the provided collection of fields
|
||||
// and the collection of fields represented by the given fragment.
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fieldMap2); // (E) Then collect any conflicts between the provided collection of fields
|
||||
// and any fragment names found in the given fragment.
|
||||
|
||||
for (var i = 0; i < fragmentNames2.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentNames2[i]);
|
||||
}
|
||||
} // Collect all conflicts found between two fragments, including via spreading in
|
||||
// any nested fragments.
|
||||
|
||||
|
||||
function collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentName2) {
|
||||
// No need to compare a fragment to itself.
|
||||
if (fragmentName1 === fragmentName2) {
|
||||
return;
|
||||
} // Memoize so two fragments are not compared for conflicts more than once.
|
||||
|
||||
|
||||
if (comparedFragmentPairs.has(fragmentName1, fragmentName2, areMutuallyExclusive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive);
|
||||
var fragment1 = context.getFragment(fragmentName1);
|
||||
var fragment2 = context.getFragment(fragmentName2);
|
||||
|
||||
if (!fragment1 || !fragment2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _getReferencedFieldsA2 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment1),
|
||||
fieldMap1 = _getReferencedFieldsA2[0],
|
||||
fragmentNames1 = _getReferencedFieldsA2[1];
|
||||
|
||||
var _getReferencedFieldsA3 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment2),
|
||||
fieldMap2 = _getReferencedFieldsA3[0],
|
||||
fragmentNames2 = _getReferencedFieldsA3[1]; // (F) First, collect all conflicts between these two collections of fields
|
||||
// (not including any nested fragments).
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (G) Then collect conflicts between the first fragment and any nested
|
||||
// fragments spread in the second fragment.
|
||||
|
||||
for (var j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentNames2[j]);
|
||||
} // (G) Then collect conflicts between the second fragment and any nested
|
||||
// fragments spread in the first fragment.
|
||||
|
||||
|
||||
for (var i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[i], fragmentName2);
|
||||
}
|
||||
} // Find all conflicts found between two selection sets, including those found
|
||||
// via spreading in fragments. Called when determining if conflicts exist
|
||||
// between the sub-fields of two overlapping fields.
|
||||
|
||||
|
||||
function findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, parentType1, selectionSet1, parentType2, selectionSet2) {
|
||||
var conflicts = [];
|
||||
|
||||
var _getFieldsAndFragment2 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType1, selectionSet1),
|
||||
fieldMap1 = _getFieldsAndFragment2[0],
|
||||
fragmentNames1 = _getFieldsAndFragment2[1];
|
||||
|
||||
var _getFieldsAndFragment3 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType2, selectionSet2),
|
||||
fieldMap2 = _getFieldsAndFragment3[0],
|
||||
fragmentNames2 = _getFieldsAndFragment3[1]; // (H) First, collect all conflicts between these two collections of field.
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (I) Then collect conflicts between the first collection of fields and
|
||||
// those referenced by each fragment name associated with the second.
|
||||
|
||||
if (fragmentNames2.length !== 0) {
|
||||
var comparedFragments = Object.create(null);
|
||||
|
||||
for (var j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fragmentNames2[j]);
|
||||
}
|
||||
} // (I) Then collect conflicts between the second collection of fields and
|
||||
// those referenced by each fragment name associated with the first.
|
||||
|
||||
|
||||
if (fragmentNames1.length !== 0) {
|
||||
var _comparedFragments = Object.create(null);
|
||||
|
||||
for (var i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, _comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap2, fragmentNames1[i]);
|
||||
}
|
||||
} // (J) Also collect conflicts between any fragment names by the first and
|
||||
// fragment names by the second. This compares each item in the first set of
|
||||
// names to each item in the second set of names.
|
||||
|
||||
|
||||
for (var _i2 = 0; _i2 < fragmentNames1.length; _i2++) {
|
||||
for (var _j = 0; _j < fragmentNames2.length; _j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[_i2], fragmentNames2[_j]);
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
} // Collect all Conflicts "within" one collection of fields.
|
||||
|
||||
|
||||
function collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap) {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For every response name, if there are multiple fields, they
|
||||
// must be compared to find a potential conflict.
|
||||
var _arr = Object.keys(fieldMap);
|
||||
|
||||
for (var _i3 = 0; _i3 < _arr.length; _i3++) {
|
||||
var responseName = _arr[_i3];
|
||||
var fields = fieldMap[responseName]; // This compares every field in the list to every other field in this list
|
||||
// (except to itself). If the list only has one item, nothing needs to
|
||||
// be compared.
|
||||
|
||||
if (fields.length > 1) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
for (var j = i + 1; j < fields.length; j++) {
|
||||
var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, // within one collection is never mutually exclusive
|
||||
responseName, fields[i], fields[j]);
|
||||
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Collect all Conflicts between two collections of fields. This is similar to,
|
||||
// but different from the `collectConflictsWithin` function above. This check
|
||||
// assumes that `collectConflictsWithin` has already been called on each
|
||||
// provided collection of fields. This is true because this validator traverses
|
||||
// each individual selection set.
|
||||
|
||||
|
||||
function collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, fieldMap1, fieldMap2) {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For any response name which appears in both provided field
|
||||
// maps, each field from the first field map must be compared to every field
|
||||
// in the second field map to find potential conflicts.
|
||||
var _arr2 = Object.keys(fieldMap1);
|
||||
|
||||
for (var _i4 = 0; _i4 < _arr2.length; _i4++) {
|
||||
var responseName = _arr2[_i4];
|
||||
var fields2 = fieldMap2[responseName];
|
||||
|
||||
if (fields2) {
|
||||
var fields1 = fieldMap1[responseName];
|
||||
|
||||
for (var i = 0; i < fields1.length; i++) {
|
||||
for (var j = 0; j < fields2.length; j++) {
|
||||
var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[j]);
|
||||
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Determines if there is a conflict between two particular fields, including
|
||||
// comparing their sub-fields.
|
||||
|
||||
|
||||
function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, field1, field2) {
|
||||
var parentType1 = field1[0],
|
||||
node1 = field1[1],
|
||||
def1 = field1[2];
|
||||
var parentType2 = field2[0],
|
||||
node2 = field2[1],
|
||||
def2 = field2[2]; // If it is known that two fields could not possibly apply at the same
|
||||
// time, due to the parent types, then it is safe to permit them to diverge
|
||||
// in aliased field or arguments used as they will not present any ambiguity
|
||||
// by differing.
|
||||
// It is known that two parent types could never overlap if they are
|
||||
// different Object types. Interface or Union types might overlap - if not
|
||||
// in the current state of the schema, then perhaps in some future version,
|
||||
// thus may not safely diverge.
|
||||
|
||||
var areMutuallyExclusive = parentFieldsAreMutuallyExclusive || parentType1 !== parentType2 && (0, _definition.isObjectType)(parentType1) && (0, _definition.isObjectType)(parentType2); // The return type for each field.
|
||||
|
||||
var type1 = def1 && def1.type;
|
||||
var type2 = def2 && def2.type;
|
||||
|
||||
if (!areMutuallyExclusive) {
|
||||
// Two aliases must refer to the same field.
|
||||
var name1 = node1.name.value;
|
||||
var name2 = node2.name.value;
|
||||
|
||||
if (name1 !== name2) {
|
||||
return [[responseName, "".concat(name1, " and ").concat(name2, " are different fields")], [node1], [node2]];
|
||||
} // Two field calls must have the same arguments.
|
||||
|
||||
|
||||
if (!sameArguments(node1.arguments || [], node2.arguments || [])) {
|
||||
return [[responseName, 'they have differing arguments'], [node1], [node2]];
|
||||
}
|
||||
}
|
||||
|
||||
if (type1 && type2 && doTypesConflict(type1, type2)) {
|
||||
return [[responseName, "they return conflicting types ".concat((0, _inspect.default)(type1), " and ").concat((0, _inspect.default)(type2))], [node1], [node2]];
|
||||
} // Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||
// for both collections so fields in a fragment reference are never
|
||||
// compared to themselves.
|
||||
|
||||
|
||||
var selectionSet1 = node1.selectionSet;
|
||||
var selectionSet2 = node2.selectionSet;
|
||||
|
||||
if (selectionSet1 && selectionSet2) {
|
||||
var conflicts = findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, (0, _definition.getNamedType)(type1), selectionSet1, (0, _definition.getNamedType)(type2), selectionSet2);
|
||||
return subfieldConflicts(conflicts, responseName, node1, node2);
|
||||
}
|
||||
}
|
||||
|
||||
function sameArguments(arguments1, arguments2) {
|
||||
if (arguments1.length !== arguments2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arguments1.every(function (argument1) {
|
||||
var argument2 = (0, _find.default)(arguments2, function (argument) {
|
||||
return argument.name.value === argument1.name.value;
|
||||
});
|
||||
|
||||
if (!argument2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sameValue(argument1.value, argument2.value);
|
||||
});
|
||||
}
|
||||
|
||||
function sameValue(value1, value2) {
|
||||
return !value1 && !value2 || (0, _printer.print)(value1) === (0, _printer.print)(value2);
|
||||
} // Two types conflict if both types could not apply to a value simultaneously.
|
||||
// Composite types are ignored as their individual field types will be compared
|
||||
// later recursively. However List and Non-Null types must match.
|
||||
|
||||
|
||||
function doTypesConflict(type1, type2) {
|
||||
if ((0, _definition.isListType)(type1)) {
|
||||
return (0, _definition.isListType)(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
|
||||
}
|
||||
|
||||
if ((0, _definition.isListType)(type2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((0, _definition.isNonNullType)(type1)) {
|
||||
return (0, _definition.isNonNullType)(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
|
||||
}
|
||||
|
||||
if ((0, _definition.isNonNullType)(type2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((0, _definition.isLeafType)(type1) || (0, _definition.isLeafType)(type2)) {
|
||||
return type1 !== type2;
|
||||
}
|
||||
|
||||
return false;
|
||||
} // Given a selection set, return the collection of fields (a mapping of response
|
||||
// name to field nodes and definitions) as well as a list of fragment names
|
||||
// referenced via fragment spreads.
|
||||
|
||||
|
||||
function getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet) {
|
||||
var cached = cachedFieldsAndFragmentNames.get(selectionSet);
|
||||
|
||||
if (!cached) {
|
||||
var nodeAndDefs = Object.create(null);
|
||||
var fragmentNames = Object.create(null);
|
||||
|
||||
_collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames);
|
||||
|
||||
cached = [nodeAndDefs, Object.keys(fragmentNames)];
|
||||
cachedFieldsAndFragmentNames.set(selectionSet, cached);
|
||||
}
|
||||
|
||||
return cached;
|
||||
} // Given a reference to a fragment, return the represented collection of fields
|
||||
// as well as a list of nested fragment names referenced via fragment spreads.
|
||||
|
||||
|
||||
function getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment) {
|
||||
// Short-circuit building a type from the node if possible.
|
||||
var cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
var fragmentType = (0, _typeFromAST.typeFromAST)(context.getSchema(), fragment.typeCondition);
|
||||
return getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragmentType, fragment.selectionSet);
|
||||
}
|
||||
|
||||
function _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames) {
|
||||
for (var i = 0; i < selectionSet.selections.length; i++) {
|
||||
var selection = selectionSet.selections[i];
|
||||
|
||||
switch (selection.kind) {
|
||||
case _kinds.Kind.FIELD:
|
||||
var fieldName = selection.name.value;
|
||||
var fieldDef = void 0;
|
||||
|
||||
if ((0, _definition.isObjectType)(parentType) || (0, _definition.isInterfaceType)(parentType)) {
|
||||
fieldDef = parentType.getFields()[fieldName];
|
||||
}
|
||||
|
||||
var responseName = selection.alias ? selection.alias.value : fieldName;
|
||||
|
||||
if (!nodeAndDefs[responseName]) {
|
||||
nodeAndDefs[responseName] = [];
|
||||
}
|
||||
|
||||
nodeAndDefs[responseName].push([parentType, selection, fieldDef]);
|
||||
break;
|
||||
|
||||
case _kinds.Kind.FRAGMENT_SPREAD:
|
||||
fragmentNames[selection.name.value] = true;
|
||||
break;
|
||||
|
||||
case _kinds.Kind.INLINE_FRAGMENT:
|
||||
var typeCondition = selection.typeCondition;
|
||||
var inlineFragmentType = typeCondition ? (0, _typeFromAST.typeFromAST)(context.getSchema(), typeCondition) : parentType;
|
||||
|
||||
_collectFieldsAndFragmentNames(context, inlineFragmentType, selection.selectionSet, nodeAndDefs, fragmentNames);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // Given a series of Conflicts which occurred between two sub-fields, generate
|
||||
// a single Conflict.
|
||||
|
||||
|
||||
function subfieldConflicts(conflicts, responseName, node1, node2) {
|
||||
if (conflicts.length > 0) {
|
||||
return [[responseName, conflicts.map(function (_ref4) {
|
||||
var reason = _ref4[0];
|
||||
return reason;
|
||||
})], conflicts.reduce(function (allFields, _ref5) {
|
||||
var fields1 = _ref5[1];
|
||||
return allFields.concat(fields1);
|
||||
}, [node1]), conflicts.reduce(function (allFields, _ref6) {
|
||||
var fields2 = _ref6[2];
|
||||
return allFields.concat(fields2);
|
||||
}, [node2])];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A way to keep track of pairs of things when the ordering of the pair does
|
||||
* not matter. We do this by maintaining a sort of double adjacency sets.
|
||||
*/
|
||||
|
||||
|
||||
var PairSet =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function PairSet() {
|
||||
_defineProperty(this, "_data", void 0);
|
||||
|
||||
this._data = Object.create(null);
|
||||
}
|
||||
|
||||
var _proto = PairSet.prototype;
|
||||
|
||||
_proto.has = function has(a, b, areMutuallyExclusive) {
|
||||
var first = this._data[a];
|
||||
var result = first && first[b];
|
||||
|
||||
if (result === undefined) {
|
||||
return false;
|
||||
} // areMutuallyExclusive being false is a superset of being true,
|
||||
// hence if we want to know if this PairSet "has" these two with no
|
||||
// exclusivity, we have to ensure it was added as such.
|
||||
|
||||
|
||||
if (areMutuallyExclusive === false) {
|
||||
return result === false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
_proto.add = function add(a, b, areMutuallyExclusive) {
|
||||
_pairSetAdd(this._data, a, b, areMutuallyExclusive);
|
||||
|
||||
_pairSetAdd(this._data, b, a, areMutuallyExclusive);
|
||||
};
|
||||
|
||||
return PairSet;
|
||||
}();
|
||||
|
||||
function _pairSetAdd(data, a, b, areMutuallyExclusive) {
|
||||
var map = data[a];
|
||||
|
||||
if (!map) {
|
||||
map = Object.create(null);
|
||||
data[a] = map;
|
||||
}
|
||||
|
||||
map[b] = areMutuallyExclusive;
|
||||
}
|
||||
Generated
Vendored
+848
@@ -0,0 +1,848 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import find from '../../jsutils/find';
|
||||
import type { ObjMap } from '../../jsutils/ObjMap';
|
||||
import type {
|
||||
SelectionSetNode,
|
||||
FieldNode,
|
||||
ArgumentNode,
|
||||
FragmentDefinitionNode,
|
||||
} from '../../language/ast';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { print } from '../../language/printer';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import {
|
||||
getNamedType,
|
||||
isNonNullType,
|
||||
isLeafType,
|
||||
isObjectType,
|
||||
isListType,
|
||||
isInterfaceType,
|
||||
} from '../../type/definition';
|
||||
import type {
|
||||
GraphQLNamedType,
|
||||
GraphQLOutputType,
|
||||
GraphQLCompositeType,
|
||||
GraphQLField,
|
||||
} from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
|
||||
export function fieldsConflictMessage(
|
||||
responseName: string,
|
||||
reason: ConflictReasonMessage,
|
||||
): string {
|
||||
return (
|
||||
`Fields "${responseName}" conflict because ${reasonMessage(reason)}` +
|
||||
'. Use different aliases on the fields to fetch both if this was ' +
|
||||
'intentional.'
|
||||
);
|
||||
}
|
||||
|
||||
function reasonMessage(reason: ConflictReasonMessage): string {
|
||||
if (Array.isArray(reason)) {
|
||||
return reason
|
||||
.map(
|
||||
([responseName, subreason]) =>
|
||||
`subfields "${responseName}" conflict because ${reasonMessage(
|
||||
subreason,
|
||||
)}`,
|
||||
)
|
||||
.join(' and ');
|
||||
}
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overlapping fields can be merged
|
||||
*
|
||||
* A selection set is only valid if all fields (including spreading any
|
||||
* fragments) either correspond to distinct response names or can be merged
|
||||
* without ambiguity.
|
||||
*/
|
||||
export function OverlappingFieldsCanBeMerged(
|
||||
context: ValidationContext,
|
||||
): ASTVisitor {
|
||||
// A memoization for when two fragments are compared "between" each other for
|
||||
// conflicts. Two fragments may be compared many times, so memoizing this can
|
||||
// dramatically improve the performance of this validator.
|
||||
const comparedFragmentPairs = new PairSet();
|
||||
|
||||
// A cache for the "field map" and list of fragment names found in any given
|
||||
// selection set. Selection sets may be asked for this information multiple
|
||||
// times, so this improves the performance of this validator.
|
||||
const cachedFieldsAndFragmentNames = new Map();
|
||||
|
||||
return {
|
||||
SelectionSet(selectionSet) {
|
||||
const conflicts = findConflictsWithinSelectionSet(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
context.getParentType(),
|
||||
selectionSet,
|
||||
);
|
||||
for (const [[responseName, reason], fields1, fields2] of conflicts) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
fieldsConflictMessage(responseName, reason),
|
||||
fields1.concat(fields2),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type Conflict = [ConflictReason, Array<FieldNode>, Array<FieldNode>];
|
||||
// Field name and reason.
|
||||
type ConflictReason = [string, ConflictReasonMessage];
|
||||
// Reason is a string, or a nested list of conflicts.
|
||||
type ConflictReasonMessage = string | Array<ConflictReason>;
|
||||
// Tuple defining a field node in a context.
|
||||
type NodeAndDef = [GraphQLCompositeType, FieldNode, ?GraphQLField<*, *>];
|
||||
// Map of array of those.
|
||||
type NodeAndDefCollection = ObjMap<Array<NodeAndDef>>;
|
||||
|
||||
/**
|
||||
* Algorithm:
|
||||
*
|
||||
* Conflicts occur when two fields exist in a query which will produce the same
|
||||
* response name, but represent differing values, thus creating a conflict.
|
||||
* The algorithm below finds all conflicts via making a series of comparisons
|
||||
* between fields. In order to compare as few fields as possible, this makes
|
||||
* a series of comparisons "within" sets of fields and "between" sets of fields.
|
||||
*
|
||||
* Given any selection set, a collection produces both a set of fields by
|
||||
* also including all inline fragments, as well as a list of fragments
|
||||
* referenced by fragment spreads.
|
||||
*
|
||||
* A) Each selection set represented in the document first compares "within" its
|
||||
* collected set of fields, finding any conflicts between every pair of
|
||||
* overlapping fields.
|
||||
* Note: This is the *only time* that a the fields "within" a set are compared
|
||||
* to each other. After this only fields "between" sets are compared.
|
||||
*
|
||||
* B) Also, if any fragment is referenced in a selection set, then a
|
||||
* comparison is made "between" the original set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* C) Also, if multiple fragments are referenced, then comparisons
|
||||
* are made "between" each referenced fragment.
|
||||
*
|
||||
* D) When comparing "between" a set of fields and a referenced fragment, first
|
||||
* a comparison is made between each field in the original set of fields and
|
||||
* each field in the the referenced set of fields.
|
||||
*
|
||||
* E) Also, if any fragment is referenced in the referenced selection set,
|
||||
* then a comparison is made "between" the original set of fields and the
|
||||
* referenced fragment (recursively referring to step D).
|
||||
*
|
||||
* F) When comparing "between" two fragments, first a comparison is made between
|
||||
* each field in the first referenced set of fields and each field in the the
|
||||
* second referenced set of fields.
|
||||
*
|
||||
* G) Also, any fragments referenced by the first must be compared to the
|
||||
* second, and any fragments referenced by the second must be compared to the
|
||||
* first (recursively referring to step F).
|
||||
*
|
||||
* H) When comparing two fields, if both have selection sets, then a comparison
|
||||
* is made "between" both selection sets, first comparing the set of fields in
|
||||
* the first selection set with the set of fields in the second.
|
||||
*
|
||||
* I) Also, if any fragment is referenced in either selection set, then a
|
||||
* comparison is made "between" the other set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* J) Also, if two fragments are referenced in both selection sets, then a
|
||||
* comparison is made "between" the two fragments.
|
||||
*
|
||||
*/
|
||||
|
||||
// Find all conflicts found "within" a selection set, including those found
|
||||
// via spreading in fragments. Called when visiting each SelectionSet in the
|
||||
// GraphQL Document.
|
||||
function findConflictsWithinSelectionSet(
|
||||
context: ValidationContext,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
parentType: ?GraphQLNamedType,
|
||||
selectionSet: SelectionSetNode,
|
||||
): Array<Conflict> {
|
||||
const conflicts = [];
|
||||
|
||||
const [fieldMap, fragmentNames] = getFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
parentType,
|
||||
selectionSet,
|
||||
);
|
||||
|
||||
// (A) Find find all conflicts "within" the fields of this selection set.
|
||||
// Note: this is the *only place* `collectConflictsWithin` is called.
|
||||
collectConflictsWithin(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
fieldMap,
|
||||
);
|
||||
|
||||
if (fragmentNames.length !== 0) {
|
||||
// (B) Then collect conflicts between these fields and those represented by
|
||||
// each spread fragment name found.
|
||||
const comparedFragments = Object.create(null);
|
||||
for (let i = 0; i < fragmentNames.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragments,
|
||||
comparedFragmentPairs,
|
||||
false,
|
||||
fieldMap,
|
||||
fragmentNames[i],
|
||||
);
|
||||
// (C) Then compare this fragment with all other fragments found in this
|
||||
// selection set to collect conflicts between fragments spread together.
|
||||
// This compares each item in the list of fragment names to every other
|
||||
// item in that same list (except for itself).
|
||||
for (let j = i + 1; j < fragmentNames.length; j++) {
|
||||
collectConflictsBetweenFragments(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
false,
|
||||
fragmentNames[i],
|
||||
fragmentNames[j],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// Collect all conflicts found between a set of fields and a fragment reference
|
||||
// including via spreading in any nested fragments.
|
||||
function collectConflictsBetweenFieldsAndFragment(
|
||||
context: ValidationContext,
|
||||
conflicts: Array<Conflict>,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragments: ObjMap<boolean>,
|
||||
comparedFragmentPairs: PairSet,
|
||||
areMutuallyExclusive: boolean,
|
||||
fieldMap: NodeAndDefCollection,
|
||||
fragmentName: string,
|
||||
): void {
|
||||
// Memoize so a fragment is not compared for conflicts more than once.
|
||||
if (comparedFragments[fragmentName]) {
|
||||
return;
|
||||
}
|
||||
comparedFragments[fragmentName] = true;
|
||||
|
||||
const fragment = context.getFragment(fragmentName);
|
||||
if (!fragment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [fieldMap2, fragmentNames2] = getReferencedFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
fragment,
|
||||
);
|
||||
|
||||
// Do not compare a fragment's fieldMap to itself.
|
||||
if (fieldMap === fieldMap2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// (D) First collect any conflicts between the provided collection of fields
|
||||
// and the collection of fields represented by the given fragment.
|
||||
collectConflictsBetween(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap,
|
||||
fieldMap2,
|
||||
);
|
||||
|
||||
// (E) Then collect any conflicts between the provided collection of fields
|
||||
// and any fragment names found in the given fragment.
|
||||
for (let i = 0; i < fragmentNames2.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragments,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap,
|
||||
fragmentNames2[i],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all conflicts found between two fragments, including via spreading in
|
||||
// any nested fragments.
|
||||
function collectConflictsBetweenFragments(
|
||||
context: ValidationContext,
|
||||
conflicts: Array<Conflict>,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
areMutuallyExclusive: boolean,
|
||||
fragmentName1: string,
|
||||
fragmentName2: string,
|
||||
): void {
|
||||
// No need to compare a fragment to itself.
|
||||
if (fragmentName1 === fragmentName2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Memoize so two fragments are not compared for conflicts more than once.
|
||||
if (
|
||||
comparedFragmentPairs.has(
|
||||
fragmentName1,
|
||||
fragmentName2,
|
||||
areMutuallyExclusive,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive);
|
||||
|
||||
const fragment1 = context.getFragment(fragmentName1);
|
||||
const fragment2 = context.getFragment(fragmentName2);
|
||||
if (!fragment1 || !fragment2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [fieldMap1, fragmentNames1] = getReferencedFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
fragment1,
|
||||
);
|
||||
const [fieldMap2, fragmentNames2] = getReferencedFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
fragment2,
|
||||
);
|
||||
|
||||
// (F) First, collect all conflicts between these two collections of fields
|
||||
// (not including any nested fragments).
|
||||
collectConflictsBetween(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap1,
|
||||
fieldMap2,
|
||||
);
|
||||
|
||||
// (G) Then collect conflicts between the first fragment and any nested
|
||||
// fragments spread in the second fragment.
|
||||
for (let j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFragments(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fragmentName1,
|
||||
fragmentNames2[j],
|
||||
);
|
||||
}
|
||||
|
||||
// (G) Then collect conflicts between the second fragment and any nested
|
||||
// fragments spread in the first fragment.
|
||||
for (let i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFragments(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fragmentNames1[i],
|
||||
fragmentName2,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Find all conflicts found between two selection sets, including those found
|
||||
// via spreading in fragments. Called when determining if conflicts exist
|
||||
// between the sub-fields of two overlapping fields.
|
||||
function findConflictsBetweenSubSelectionSets(
|
||||
context: ValidationContext,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
areMutuallyExclusive: boolean,
|
||||
parentType1: ?GraphQLNamedType,
|
||||
selectionSet1: SelectionSetNode,
|
||||
parentType2: ?GraphQLNamedType,
|
||||
selectionSet2: SelectionSetNode,
|
||||
): Array<Conflict> {
|
||||
const conflicts = [];
|
||||
|
||||
const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
parentType1,
|
||||
selectionSet1,
|
||||
);
|
||||
const [fieldMap2, fragmentNames2] = getFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
parentType2,
|
||||
selectionSet2,
|
||||
);
|
||||
|
||||
// (H) First, collect all conflicts between these two collections of field.
|
||||
collectConflictsBetween(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap1,
|
||||
fieldMap2,
|
||||
);
|
||||
|
||||
// (I) Then collect conflicts between the first collection of fields and
|
||||
// those referenced by each fragment name associated with the second.
|
||||
if (fragmentNames2.length !== 0) {
|
||||
const comparedFragments = Object.create(null);
|
||||
for (let j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFieldsAndFragment(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragments,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap1,
|
||||
fragmentNames2[j],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// (I) Then collect conflicts between the second collection of fields and
|
||||
// those referenced by each fragment name associated with the first.
|
||||
if (fragmentNames1.length !== 0) {
|
||||
const comparedFragments = Object.create(null);
|
||||
for (let i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragments,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fieldMap2,
|
||||
fragmentNames1[i],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// (J) Also collect conflicts between any fragment names by the first and
|
||||
// fragment names by the second. This compares each item in the first set of
|
||||
// names to each item in the second set of names.
|
||||
for (let i = 0; i < fragmentNames1.length; i++) {
|
||||
for (let j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFragments(
|
||||
context,
|
||||
conflicts,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
fragmentNames1[i],
|
||||
fragmentNames2[j],
|
||||
);
|
||||
}
|
||||
}
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// Collect all Conflicts "within" one collection of fields.
|
||||
function collectConflictsWithin(
|
||||
context: ValidationContext,
|
||||
conflicts: Array<Conflict>,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
fieldMap: NodeAndDefCollection,
|
||||
): void {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For every response name, if there are multiple fields, they
|
||||
// must be compared to find a potential conflict.
|
||||
for (const responseName of Object.keys(fieldMap)) {
|
||||
const fields = fieldMap[responseName];
|
||||
// This compares every field in the list to every other field in this list
|
||||
// (except to itself). If the list only has one item, nothing needs to
|
||||
// be compared.
|
||||
if (fields.length > 1) {
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
for (let j = i + 1; j < fields.length; j++) {
|
||||
const conflict = findConflict(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
false, // within one collection is never mutually exclusive
|
||||
responseName,
|
||||
fields[i],
|
||||
fields[j],
|
||||
);
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all Conflicts between two collections of fields. This is similar to,
|
||||
// but different from the `collectConflictsWithin` function above. This check
|
||||
// assumes that `collectConflictsWithin` has already been called on each
|
||||
// provided collection of fields. This is true because this validator traverses
|
||||
// each individual selection set.
|
||||
function collectConflictsBetween(
|
||||
context: ValidationContext,
|
||||
conflicts: Array<Conflict>,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
parentFieldsAreMutuallyExclusive: boolean,
|
||||
fieldMap1: NodeAndDefCollection,
|
||||
fieldMap2: NodeAndDefCollection,
|
||||
): void {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For any response name which appears in both provided field
|
||||
// maps, each field from the first field map must be compared to every field
|
||||
// in the second field map to find potential conflicts.
|
||||
for (const responseName of Object.keys(fieldMap1)) {
|
||||
const fields2 = fieldMap2[responseName];
|
||||
if (fields2) {
|
||||
const fields1 = fieldMap1[responseName];
|
||||
for (let i = 0; i < fields1.length; i++) {
|
||||
for (let j = 0; j < fields2.length; j++) {
|
||||
const conflict = findConflict(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
parentFieldsAreMutuallyExclusive,
|
||||
responseName,
|
||||
fields1[i],
|
||||
fields2[j],
|
||||
);
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determines if there is a conflict between two particular fields, including
|
||||
// comparing their sub-fields.
|
||||
function findConflict(
|
||||
context: ValidationContext,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs: PairSet,
|
||||
parentFieldsAreMutuallyExclusive: boolean,
|
||||
responseName: string,
|
||||
field1: NodeAndDef,
|
||||
field2: NodeAndDef,
|
||||
): ?Conflict {
|
||||
const [parentType1, node1, def1] = field1;
|
||||
const [parentType2, node2, def2] = field2;
|
||||
|
||||
// If it is known that two fields could not possibly apply at the same
|
||||
// time, due to the parent types, then it is safe to permit them to diverge
|
||||
// in aliased field or arguments used as they will not present any ambiguity
|
||||
// by differing.
|
||||
// It is known that two parent types could never overlap if they are
|
||||
// different Object types. Interface or Union types might overlap - if not
|
||||
// in the current state of the schema, then perhaps in some future version,
|
||||
// thus may not safely diverge.
|
||||
const areMutuallyExclusive =
|
||||
parentFieldsAreMutuallyExclusive ||
|
||||
(parentType1 !== parentType2 &&
|
||||
isObjectType(parentType1) &&
|
||||
isObjectType(parentType2));
|
||||
|
||||
// The return type for each field.
|
||||
const type1 = def1 && def1.type;
|
||||
const type2 = def2 && def2.type;
|
||||
|
||||
if (!areMutuallyExclusive) {
|
||||
// Two aliases must refer to the same field.
|
||||
const name1 = node1.name.value;
|
||||
const name2 = node2.name.value;
|
||||
if (name1 !== name2) {
|
||||
return [
|
||||
[responseName, `${name1} and ${name2} are different fields`],
|
||||
[node1],
|
||||
[node2],
|
||||
];
|
||||
}
|
||||
|
||||
// Two field calls must have the same arguments.
|
||||
if (!sameArguments(node1.arguments || [], node2.arguments || [])) {
|
||||
return [
|
||||
[responseName, 'they have differing arguments'],
|
||||
[node1],
|
||||
[node2],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (type1 && type2 && doTypesConflict(type1, type2)) {
|
||||
return [
|
||||
[
|
||||
responseName,
|
||||
`they return conflicting types ${inspect(type1)} and ${inspect(type2)}`,
|
||||
],
|
||||
[node1],
|
||||
[node2],
|
||||
];
|
||||
}
|
||||
|
||||
// Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||
// for both collections so fields in a fragment reference are never
|
||||
// compared to themselves.
|
||||
const selectionSet1 = node1.selectionSet;
|
||||
const selectionSet2 = node2.selectionSet;
|
||||
if (selectionSet1 && selectionSet2) {
|
||||
const conflicts = findConflictsBetweenSubSelectionSets(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
comparedFragmentPairs,
|
||||
areMutuallyExclusive,
|
||||
getNamedType(type1),
|
||||
selectionSet1,
|
||||
getNamedType(type2),
|
||||
selectionSet2,
|
||||
);
|
||||
return subfieldConflicts(conflicts, responseName, node1, node2);
|
||||
}
|
||||
}
|
||||
|
||||
function sameArguments(
|
||||
arguments1: $ReadOnlyArray<ArgumentNode>,
|
||||
arguments2: $ReadOnlyArray<ArgumentNode>,
|
||||
): boolean {
|
||||
if (arguments1.length !== arguments2.length) {
|
||||
return false;
|
||||
}
|
||||
return arguments1.every(argument1 => {
|
||||
const argument2 = find(
|
||||
arguments2,
|
||||
argument => argument.name.value === argument1.name.value,
|
||||
);
|
||||
if (!argument2) {
|
||||
return false;
|
||||
}
|
||||
return sameValue(argument1.value, argument2.value);
|
||||
});
|
||||
}
|
||||
|
||||
function sameValue(value1, value2) {
|
||||
return (!value1 && !value2) || print(value1) === print(value2);
|
||||
}
|
||||
|
||||
// Two types conflict if both types could not apply to a value simultaneously.
|
||||
// Composite types are ignored as their individual field types will be compared
|
||||
// later recursively. However List and Non-Null types must match.
|
||||
function doTypesConflict(
|
||||
type1: GraphQLOutputType,
|
||||
type2: GraphQLOutputType,
|
||||
): boolean {
|
||||
if (isListType(type1)) {
|
||||
return isListType(type2)
|
||||
? doTypesConflict(type1.ofType, type2.ofType)
|
||||
: true;
|
||||
}
|
||||
if (isListType(type2)) {
|
||||
return true;
|
||||
}
|
||||
if (isNonNullType(type1)) {
|
||||
return isNonNullType(type2)
|
||||
? doTypesConflict(type1.ofType, type2.ofType)
|
||||
: true;
|
||||
}
|
||||
if (isNonNullType(type2)) {
|
||||
return true;
|
||||
}
|
||||
if (isLeafType(type1) || isLeafType(type2)) {
|
||||
return type1 !== type2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Given a selection set, return the collection of fields (a mapping of response
|
||||
// name to field nodes and definitions) as well as a list of fragment names
|
||||
// referenced via fragment spreads.
|
||||
function getFieldsAndFragmentNames(
|
||||
context: ValidationContext,
|
||||
cachedFieldsAndFragmentNames,
|
||||
parentType: ?GraphQLNamedType,
|
||||
selectionSet: SelectionSetNode,
|
||||
): [NodeAndDefCollection, Array<string>] {
|
||||
let cached = cachedFieldsAndFragmentNames.get(selectionSet);
|
||||
if (!cached) {
|
||||
const nodeAndDefs = Object.create(null);
|
||||
const fragmentNames = Object.create(null);
|
||||
_collectFieldsAndFragmentNames(
|
||||
context,
|
||||
parentType,
|
||||
selectionSet,
|
||||
nodeAndDefs,
|
||||
fragmentNames,
|
||||
);
|
||||
cached = [nodeAndDefs, Object.keys(fragmentNames)];
|
||||
cachedFieldsAndFragmentNames.set(selectionSet, cached);
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Given a reference to a fragment, return the represented collection of fields
|
||||
// as well as a list of nested fragment names referenced via fragment spreads.
|
||||
function getReferencedFieldsAndFragmentNames(
|
||||
context: ValidationContext,
|
||||
cachedFieldsAndFragmentNames,
|
||||
fragment: FragmentDefinitionNode,
|
||||
) {
|
||||
// Short-circuit building a type from the node if possible.
|
||||
const cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const fragmentType = typeFromAST(context.getSchema(), fragment.typeCondition);
|
||||
return getFieldsAndFragmentNames(
|
||||
context,
|
||||
cachedFieldsAndFragmentNames,
|
||||
fragmentType,
|
||||
fragment.selectionSet,
|
||||
);
|
||||
}
|
||||
|
||||
function _collectFieldsAndFragmentNames(
|
||||
context: ValidationContext,
|
||||
parentType: ?GraphQLNamedType,
|
||||
selectionSet: SelectionSetNode,
|
||||
nodeAndDefs,
|
||||
fragmentNames,
|
||||
): void {
|
||||
for (let i = 0; i < selectionSet.selections.length; i++) {
|
||||
const selection = selectionSet.selections[i];
|
||||
switch (selection.kind) {
|
||||
case Kind.FIELD:
|
||||
const fieldName = selection.name.value;
|
||||
let fieldDef;
|
||||
if (isObjectType(parentType) || isInterfaceType(parentType)) {
|
||||
fieldDef = parentType.getFields()[fieldName];
|
||||
}
|
||||
const responseName = selection.alias
|
||||
? selection.alias.value
|
||||
: fieldName;
|
||||
if (!nodeAndDefs[responseName]) {
|
||||
nodeAndDefs[responseName] = [];
|
||||
}
|
||||
nodeAndDefs[responseName].push([parentType, selection, fieldDef]);
|
||||
break;
|
||||
case Kind.FRAGMENT_SPREAD:
|
||||
fragmentNames[selection.name.value] = true;
|
||||
break;
|
||||
case Kind.INLINE_FRAGMENT:
|
||||
const typeCondition = selection.typeCondition;
|
||||
const inlineFragmentType = typeCondition
|
||||
? typeFromAST(context.getSchema(), typeCondition)
|
||||
: parentType;
|
||||
_collectFieldsAndFragmentNames(
|
||||
context,
|
||||
inlineFragmentType,
|
||||
selection.selectionSet,
|
||||
nodeAndDefs,
|
||||
fragmentNames,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given a series of Conflicts which occurred between two sub-fields, generate
|
||||
// a single Conflict.
|
||||
function subfieldConflicts(
|
||||
conflicts: Array<Conflict>,
|
||||
responseName: string,
|
||||
node1: FieldNode,
|
||||
node2: FieldNode,
|
||||
): ?Conflict {
|
||||
if (conflicts.length > 0) {
|
||||
return [
|
||||
[responseName, conflicts.map(([reason]) => reason)],
|
||||
conflicts.reduce((allFields, [, fields1]) => allFields.concat(fields1), [
|
||||
node1,
|
||||
]),
|
||||
conflicts.reduce(
|
||||
(allFields, [, , fields2]) => allFields.concat(fields2),
|
||||
[node2],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A way to keep track of pairs of things when the ordering of the pair does
|
||||
* not matter. We do this by maintaining a sort of double adjacency sets.
|
||||
*/
|
||||
class PairSet {
|
||||
_data: ObjMap<ObjMap<boolean>>;
|
||||
|
||||
constructor(): void {
|
||||
this._data = Object.create(null);
|
||||
}
|
||||
|
||||
has(a: string, b: string, areMutuallyExclusive: boolean) {
|
||||
const first = this._data[a];
|
||||
const result = first && first[b];
|
||||
if (result === undefined) {
|
||||
return false;
|
||||
}
|
||||
// areMutuallyExclusive being false is a superset of being true,
|
||||
// hence if we want to know if this PairSet "has" these two with no
|
||||
// exclusivity, we have to ensure it was added as such.
|
||||
if (areMutuallyExclusive === false) {
|
||||
return result === false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
add(a: string, b: string, areMutuallyExclusive: boolean) {
|
||||
_pairSetAdd(this._data, a, b, areMutuallyExclusive);
|
||||
_pairSetAdd(this._data, b, a, areMutuallyExclusive);
|
||||
}
|
||||
}
|
||||
|
||||
function _pairSetAdd(data, a, b, areMutuallyExclusive) {
|
||||
let map = data[a];
|
||||
if (!map) {
|
||||
map = Object.create(null);
|
||||
data[a] = map;
|
||||
}
|
||||
map[b] = areMutuallyExclusive;
|
||||
}
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import find from '../../jsutils/find';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { print } from '../../language/printer';
|
||||
import { getNamedType, isNonNullType, isLeafType, isObjectType, isListType, isInterfaceType } from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
export function fieldsConflictMessage(responseName, reason) {
|
||||
return "Fields \"".concat(responseName, "\" conflict because ").concat(reasonMessage(reason)) + '. Use different aliases on the fields to fetch both if this was ' + 'intentional.';
|
||||
}
|
||||
|
||||
function reasonMessage(reason) {
|
||||
if (Array.isArray(reason)) {
|
||||
return reason.map(function (_ref) {
|
||||
var responseName = _ref[0],
|
||||
subreason = _ref[1];
|
||||
return "subfields \"".concat(responseName, "\" conflict because ").concat(reasonMessage(subreason));
|
||||
}).join(' and ');
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
/**
|
||||
* Overlapping fields can be merged
|
||||
*
|
||||
* A selection set is only valid if all fields (including spreading any
|
||||
* fragments) either correspond to distinct response names or can be merged
|
||||
* without ambiguity.
|
||||
*/
|
||||
|
||||
|
||||
export function OverlappingFieldsCanBeMerged(context) {
|
||||
// A memoization for when two fragments are compared "between" each other for
|
||||
// conflicts. Two fragments may be compared many times, so memoizing this can
|
||||
// dramatically improve the performance of this validator.
|
||||
var comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given
|
||||
// selection set. Selection sets may be asked for this information multiple
|
||||
// times, so this improves the performance of this validator.
|
||||
|
||||
var cachedFieldsAndFragmentNames = new Map();
|
||||
return {
|
||||
SelectionSet: function SelectionSet(selectionSet) {
|
||||
var conflicts = findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, context.getParentType(), selectionSet);
|
||||
|
||||
for (var _i = 0; _i < conflicts.length; _i++) {
|
||||
var _ref3 = conflicts[_i];
|
||||
var _ref2$ = _ref3[0];
|
||||
var responseName = _ref2$[0];
|
||||
var reason = _ref2$[1];
|
||||
var fields1 = _ref3[1];
|
||||
var fields2 = _ref3[2];
|
||||
context.reportError(new GraphQLError(fieldsConflictMessage(responseName, reason), fields1.concat(fields2)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm:
|
||||
*
|
||||
* Conflicts occur when two fields exist in a query which will produce the same
|
||||
* response name, but represent differing values, thus creating a conflict.
|
||||
* The algorithm below finds all conflicts via making a series of comparisons
|
||||
* between fields. In order to compare as few fields as possible, this makes
|
||||
* a series of comparisons "within" sets of fields and "between" sets of fields.
|
||||
*
|
||||
* Given any selection set, a collection produces both a set of fields by
|
||||
* also including all inline fragments, as well as a list of fragments
|
||||
* referenced by fragment spreads.
|
||||
*
|
||||
* A) Each selection set represented in the document first compares "within" its
|
||||
* collected set of fields, finding any conflicts between every pair of
|
||||
* overlapping fields.
|
||||
* Note: This is the *only time* that a the fields "within" a set are compared
|
||||
* to each other. After this only fields "between" sets are compared.
|
||||
*
|
||||
* B) Also, if any fragment is referenced in a selection set, then a
|
||||
* comparison is made "between" the original set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* C) Also, if multiple fragments are referenced, then comparisons
|
||||
* are made "between" each referenced fragment.
|
||||
*
|
||||
* D) When comparing "between" a set of fields and a referenced fragment, first
|
||||
* a comparison is made between each field in the original set of fields and
|
||||
* each field in the the referenced set of fields.
|
||||
*
|
||||
* E) Also, if any fragment is referenced in the referenced selection set,
|
||||
* then a comparison is made "between" the original set of fields and the
|
||||
* referenced fragment (recursively referring to step D).
|
||||
*
|
||||
* F) When comparing "between" two fragments, first a comparison is made between
|
||||
* each field in the first referenced set of fields and each field in the the
|
||||
* second referenced set of fields.
|
||||
*
|
||||
* G) Also, any fragments referenced by the first must be compared to the
|
||||
* second, and any fragments referenced by the second must be compared to the
|
||||
* first (recursively referring to step F).
|
||||
*
|
||||
* H) When comparing two fields, if both have selection sets, then a comparison
|
||||
* is made "between" both selection sets, first comparing the set of fields in
|
||||
* the first selection set with the set of fields in the second.
|
||||
*
|
||||
* I) Also, if any fragment is referenced in either selection set, then a
|
||||
* comparison is made "between" the other set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* J) Also, if two fragments are referenced in both selection sets, then a
|
||||
* comparison is made "between" the two fragments.
|
||||
*
|
||||
*/
|
||||
// Find all conflicts found "within" a selection set, including those found
|
||||
// via spreading in fragments. Called when visiting each SelectionSet in the
|
||||
// GraphQL Document.
|
||||
function findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentType, selectionSet) {
|
||||
var conflicts = [];
|
||||
|
||||
var _getFieldsAndFragment = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet),
|
||||
fieldMap = _getFieldsAndFragment[0],
|
||||
fragmentNames = _getFieldsAndFragment[1]; // (A) Find find all conflicts "within" the fields of this selection set.
|
||||
// Note: this is the *only place* `collectConflictsWithin` is called.
|
||||
|
||||
|
||||
collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap);
|
||||
|
||||
if (fragmentNames.length !== 0) {
|
||||
// (B) Then collect conflicts between these fields and those represented by
|
||||
// each spread fragment name found.
|
||||
var comparedFragments = Object.create(null);
|
||||
|
||||
for (var i = 0; i < fragmentNames.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); // (C) Then compare this fragment with all other fragments found in this
|
||||
// selection set to collect conflicts between fragments spread together.
|
||||
// This compares each item in the list of fragment names to every other
|
||||
// item in that same list (except for itself).
|
||||
|
||||
for (var j = i + 1; j < fragmentNames.length; j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fragmentNames[i], fragmentNames[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
} // Collect all conflicts found between a set of fields and a fragment reference
|
||||
// including via spreading in any nested fragments.
|
||||
|
||||
|
||||
function collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentName) {
|
||||
// Memoize so a fragment is not compared for conflicts more than once.
|
||||
if (comparedFragments[fragmentName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
comparedFragments[fragmentName] = true;
|
||||
var fragment = context.getFragment(fragmentName);
|
||||
|
||||
if (!fragment) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _getReferencedFieldsA = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment),
|
||||
fieldMap2 = _getReferencedFieldsA[0],
|
||||
fragmentNames2 = _getReferencedFieldsA[1]; // Do not compare a fragment's fieldMap to itself.
|
||||
|
||||
|
||||
if (fieldMap === fieldMap2) {
|
||||
return;
|
||||
} // (D) First collect any conflicts between the provided collection of fields
|
||||
// and the collection of fields represented by the given fragment.
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fieldMap2); // (E) Then collect any conflicts between the provided collection of fields
|
||||
// and any fragment names found in the given fragment.
|
||||
|
||||
for (var i = 0; i < fragmentNames2.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentNames2[i]);
|
||||
}
|
||||
} // Collect all conflicts found between two fragments, including via spreading in
|
||||
// any nested fragments.
|
||||
|
||||
|
||||
function collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentName2) {
|
||||
// No need to compare a fragment to itself.
|
||||
if (fragmentName1 === fragmentName2) {
|
||||
return;
|
||||
} // Memoize so two fragments are not compared for conflicts more than once.
|
||||
|
||||
|
||||
if (comparedFragmentPairs.has(fragmentName1, fragmentName2, areMutuallyExclusive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive);
|
||||
var fragment1 = context.getFragment(fragmentName1);
|
||||
var fragment2 = context.getFragment(fragmentName2);
|
||||
|
||||
if (!fragment1 || !fragment2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _getReferencedFieldsA2 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment1),
|
||||
fieldMap1 = _getReferencedFieldsA2[0],
|
||||
fragmentNames1 = _getReferencedFieldsA2[1];
|
||||
|
||||
var _getReferencedFieldsA3 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment2),
|
||||
fieldMap2 = _getReferencedFieldsA3[0],
|
||||
fragmentNames2 = _getReferencedFieldsA3[1]; // (F) First, collect all conflicts between these two collections of fields
|
||||
// (not including any nested fragments).
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (G) Then collect conflicts between the first fragment and any nested
|
||||
// fragments spread in the second fragment.
|
||||
|
||||
for (var j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentNames2[j]);
|
||||
} // (G) Then collect conflicts between the second fragment and any nested
|
||||
// fragments spread in the first fragment.
|
||||
|
||||
|
||||
for (var i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[i], fragmentName2);
|
||||
}
|
||||
} // Find all conflicts found between two selection sets, including those found
|
||||
// via spreading in fragments. Called when determining if conflicts exist
|
||||
// between the sub-fields of two overlapping fields.
|
||||
|
||||
|
||||
function findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, parentType1, selectionSet1, parentType2, selectionSet2) {
|
||||
var conflicts = [];
|
||||
|
||||
var _getFieldsAndFragment2 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType1, selectionSet1),
|
||||
fieldMap1 = _getFieldsAndFragment2[0],
|
||||
fragmentNames1 = _getFieldsAndFragment2[1];
|
||||
|
||||
var _getFieldsAndFragment3 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType2, selectionSet2),
|
||||
fieldMap2 = _getFieldsAndFragment3[0],
|
||||
fragmentNames2 = _getFieldsAndFragment3[1]; // (H) First, collect all conflicts between these two collections of field.
|
||||
|
||||
|
||||
collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (I) Then collect conflicts between the first collection of fields and
|
||||
// those referenced by each fragment name associated with the second.
|
||||
|
||||
if (fragmentNames2.length !== 0) {
|
||||
var comparedFragments = Object.create(null);
|
||||
|
||||
for (var j = 0; j < fragmentNames2.length; j++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fragmentNames2[j]);
|
||||
}
|
||||
} // (I) Then collect conflicts between the second collection of fields and
|
||||
// those referenced by each fragment name associated with the first.
|
||||
|
||||
|
||||
if (fragmentNames1.length !== 0) {
|
||||
var _comparedFragments = Object.create(null);
|
||||
|
||||
for (var i = 0; i < fragmentNames1.length; i++) {
|
||||
collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, _comparedFragments, comparedFragmentPairs, areMutuallyExclusive, fieldMap2, fragmentNames1[i]);
|
||||
}
|
||||
} // (J) Also collect conflicts between any fragment names by the first and
|
||||
// fragment names by the second. This compares each item in the first set of
|
||||
// names to each item in the second set of names.
|
||||
|
||||
|
||||
for (var _i2 = 0; _i2 < fragmentNames1.length; _i2++) {
|
||||
for (var _j = 0; _j < fragmentNames2.length; _j++) {
|
||||
collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[_i2], fragmentNames2[_j]);
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
} // Collect all Conflicts "within" one collection of fields.
|
||||
|
||||
|
||||
function collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap) {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For every response name, if there are multiple fields, they
|
||||
// must be compared to find a potential conflict.
|
||||
var _arr = Object.keys(fieldMap);
|
||||
|
||||
for (var _i3 = 0; _i3 < _arr.length; _i3++) {
|
||||
var responseName = _arr[_i3];
|
||||
var fields = fieldMap[responseName]; // This compares every field in the list to every other field in this list
|
||||
// (except to itself). If the list only has one item, nothing needs to
|
||||
// be compared.
|
||||
|
||||
if (fields.length > 1) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
for (var j = i + 1; j < fields.length; j++) {
|
||||
var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, // within one collection is never mutually exclusive
|
||||
responseName, fields[i], fields[j]);
|
||||
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Collect all Conflicts between two collections of fields. This is similar to,
|
||||
// but different from the `collectConflictsWithin` function above. This check
|
||||
// assumes that `collectConflictsWithin` has already been called on each
|
||||
// provided collection of fields. This is true because this validator traverses
|
||||
// each individual selection set.
|
||||
|
||||
|
||||
function collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, fieldMap1, fieldMap2) {
|
||||
// A field map is a keyed collection, where each key represents a response
|
||||
// name and the value at that key is a list of all fields which provide that
|
||||
// response name. For any response name which appears in both provided field
|
||||
// maps, each field from the first field map must be compared to every field
|
||||
// in the second field map to find potential conflicts.
|
||||
var _arr2 = Object.keys(fieldMap1);
|
||||
|
||||
for (var _i4 = 0; _i4 < _arr2.length; _i4++) {
|
||||
var responseName = _arr2[_i4];
|
||||
var fields2 = fieldMap2[responseName];
|
||||
|
||||
if (fields2) {
|
||||
var fields1 = fieldMap1[responseName];
|
||||
|
||||
for (var i = 0; i < fields1.length; i++) {
|
||||
for (var j = 0; j < fields2.length; j++) {
|
||||
var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[j]);
|
||||
|
||||
if (conflict) {
|
||||
conflicts.push(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Determines if there is a conflict between two particular fields, including
|
||||
// comparing their sub-fields.
|
||||
|
||||
|
||||
function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, field1, field2) {
|
||||
var parentType1 = field1[0],
|
||||
node1 = field1[1],
|
||||
def1 = field1[2];
|
||||
var parentType2 = field2[0],
|
||||
node2 = field2[1],
|
||||
def2 = field2[2]; // If it is known that two fields could not possibly apply at the same
|
||||
// time, due to the parent types, then it is safe to permit them to diverge
|
||||
// in aliased field or arguments used as they will not present any ambiguity
|
||||
// by differing.
|
||||
// It is known that two parent types could never overlap if they are
|
||||
// different Object types. Interface or Union types might overlap - if not
|
||||
// in the current state of the schema, then perhaps in some future version,
|
||||
// thus may not safely diverge.
|
||||
|
||||
var areMutuallyExclusive = parentFieldsAreMutuallyExclusive || parentType1 !== parentType2 && isObjectType(parentType1) && isObjectType(parentType2); // The return type for each field.
|
||||
|
||||
var type1 = def1 && def1.type;
|
||||
var type2 = def2 && def2.type;
|
||||
|
||||
if (!areMutuallyExclusive) {
|
||||
// Two aliases must refer to the same field.
|
||||
var name1 = node1.name.value;
|
||||
var name2 = node2.name.value;
|
||||
|
||||
if (name1 !== name2) {
|
||||
return [[responseName, "".concat(name1, " and ").concat(name2, " are different fields")], [node1], [node2]];
|
||||
} // Two field calls must have the same arguments.
|
||||
|
||||
|
||||
if (!sameArguments(node1.arguments || [], node2.arguments || [])) {
|
||||
return [[responseName, 'they have differing arguments'], [node1], [node2]];
|
||||
}
|
||||
}
|
||||
|
||||
if (type1 && type2 && doTypesConflict(type1, type2)) {
|
||||
return [[responseName, "they return conflicting types ".concat(inspect(type1), " and ").concat(inspect(type2))], [node1], [node2]];
|
||||
} // Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||
// for both collections so fields in a fragment reference are never
|
||||
// compared to themselves.
|
||||
|
||||
|
||||
var selectionSet1 = node1.selectionSet;
|
||||
var selectionSet2 = node2.selectionSet;
|
||||
|
||||
if (selectionSet1 && selectionSet2) {
|
||||
var conflicts = findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, getNamedType(type1), selectionSet1, getNamedType(type2), selectionSet2);
|
||||
return subfieldConflicts(conflicts, responseName, node1, node2);
|
||||
}
|
||||
}
|
||||
|
||||
function sameArguments(arguments1, arguments2) {
|
||||
if (arguments1.length !== arguments2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arguments1.every(function (argument1) {
|
||||
var argument2 = find(arguments2, function (argument) {
|
||||
return argument.name.value === argument1.name.value;
|
||||
});
|
||||
|
||||
if (!argument2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sameValue(argument1.value, argument2.value);
|
||||
});
|
||||
}
|
||||
|
||||
function sameValue(value1, value2) {
|
||||
return !value1 && !value2 || print(value1) === print(value2);
|
||||
} // Two types conflict if both types could not apply to a value simultaneously.
|
||||
// Composite types are ignored as their individual field types will be compared
|
||||
// later recursively. However List and Non-Null types must match.
|
||||
|
||||
|
||||
function doTypesConflict(type1, type2) {
|
||||
if (isListType(type1)) {
|
||||
return isListType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
|
||||
}
|
||||
|
||||
if (isListType(type2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNonNullType(type1)) {
|
||||
return isNonNullType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
|
||||
}
|
||||
|
||||
if (isNonNullType(type2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLeafType(type1) || isLeafType(type2)) {
|
||||
return type1 !== type2;
|
||||
}
|
||||
|
||||
return false;
|
||||
} // Given a selection set, return the collection of fields (a mapping of response
|
||||
// name to field nodes and definitions) as well as a list of fragment names
|
||||
// referenced via fragment spreads.
|
||||
|
||||
|
||||
function getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet) {
|
||||
var cached = cachedFieldsAndFragmentNames.get(selectionSet);
|
||||
|
||||
if (!cached) {
|
||||
var nodeAndDefs = Object.create(null);
|
||||
var fragmentNames = Object.create(null);
|
||||
|
||||
_collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames);
|
||||
|
||||
cached = [nodeAndDefs, Object.keys(fragmentNames)];
|
||||
cachedFieldsAndFragmentNames.set(selectionSet, cached);
|
||||
}
|
||||
|
||||
return cached;
|
||||
} // Given a reference to a fragment, return the represented collection of fields
|
||||
// as well as a list of nested fragment names referenced via fragment spreads.
|
||||
|
||||
|
||||
function getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment) {
|
||||
// Short-circuit building a type from the node if possible.
|
||||
var cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
var fragmentType = typeFromAST(context.getSchema(), fragment.typeCondition);
|
||||
return getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragmentType, fragment.selectionSet);
|
||||
}
|
||||
|
||||
function _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames) {
|
||||
for (var i = 0; i < selectionSet.selections.length; i++) {
|
||||
var selection = selectionSet.selections[i];
|
||||
|
||||
switch (selection.kind) {
|
||||
case Kind.FIELD:
|
||||
var fieldName = selection.name.value;
|
||||
var fieldDef = void 0;
|
||||
|
||||
if (isObjectType(parentType) || isInterfaceType(parentType)) {
|
||||
fieldDef = parentType.getFields()[fieldName];
|
||||
}
|
||||
|
||||
var responseName = selection.alias ? selection.alias.value : fieldName;
|
||||
|
||||
if (!nodeAndDefs[responseName]) {
|
||||
nodeAndDefs[responseName] = [];
|
||||
}
|
||||
|
||||
nodeAndDefs[responseName].push([parentType, selection, fieldDef]);
|
||||
break;
|
||||
|
||||
case Kind.FRAGMENT_SPREAD:
|
||||
fragmentNames[selection.name.value] = true;
|
||||
break;
|
||||
|
||||
case Kind.INLINE_FRAGMENT:
|
||||
var typeCondition = selection.typeCondition;
|
||||
var inlineFragmentType = typeCondition ? typeFromAST(context.getSchema(), typeCondition) : parentType;
|
||||
|
||||
_collectFieldsAndFragmentNames(context, inlineFragmentType, selection.selectionSet, nodeAndDefs, fragmentNames);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // Given a series of Conflicts which occurred between two sub-fields, generate
|
||||
// a single Conflict.
|
||||
|
||||
|
||||
function subfieldConflicts(conflicts, responseName, node1, node2) {
|
||||
if (conflicts.length > 0) {
|
||||
return [[responseName, conflicts.map(function (_ref4) {
|
||||
var reason = _ref4[0];
|
||||
return reason;
|
||||
})], conflicts.reduce(function (allFields, _ref5) {
|
||||
var fields1 = _ref5[1];
|
||||
return allFields.concat(fields1);
|
||||
}, [node1]), conflicts.reduce(function (allFields, _ref6) {
|
||||
var fields2 = _ref6[2];
|
||||
return allFields.concat(fields2);
|
||||
}, [node2])];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A way to keep track of pairs of things when the ordering of the pair does
|
||||
* not matter. We do this by maintaining a sort of double adjacency sets.
|
||||
*/
|
||||
|
||||
|
||||
var PairSet =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function PairSet() {
|
||||
_defineProperty(this, "_data", void 0);
|
||||
|
||||
this._data = Object.create(null);
|
||||
}
|
||||
|
||||
var _proto = PairSet.prototype;
|
||||
|
||||
_proto.has = function has(a, b, areMutuallyExclusive) {
|
||||
var first = this._data[a];
|
||||
var result = first && first[b];
|
||||
|
||||
if (result === undefined) {
|
||||
return false;
|
||||
} // areMutuallyExclusive being false is a superset of being true,
|
||||
// hence if we want to know if this PairSet "has" these two with no
|
||||
// exclusivity, we have to ensure it was added as such.
|
||||
|
||||
|
||||
if (areMutuallyExclusive === false) {
|
||||
return result === false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
_proto.add = function add(a, b, areMutuallyExclusive) {
|
||||
_pairSetAdd(this._data, a, b, areMutuallyExclusive);
|
||||
|
||||
_pairSetAdd(this._data, b, a, areMutuallyExclusive);
|
||||
};
|
||||
|
||||
return PairSet;
|
||||
}();
|
||||
|
||||
function _pairSetAdd(data, a, b, areMutuallyExclusive) {
|
||||
var map = data[a];
|
||||
|
||||
if (!map) {
|
||||
map = Object.create(null);
|
||||
data[a] = map;
|
||||
}
|
||||
|
||||
map[b] = areMutuallyExclusive;
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.typeIncompatibleSpreadMessage = typeIncompatibleSpreadMessage;
|
||||
exports.typeIncompatibleAnonSpreadMessage = typeIncompatibleAnonSpreadMessage;
|
||||
exports.PossibleFragmentSpreads = PossibleFragmentSpreads;
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _typeComparators = require("../../utilities/typeComparators");
|
||||
|
||||
var _typeFromAST = require("../../utilities/typeFromAST");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function typeIncompatibleSpreadMessage(fragName, parentType, fragType) {
|
||||
return "Fragment \"".concat(fragName, "\" cannot be spread here as objects of ") + "type \"".concat(parentType, "\" can never be of type \"").concat(fragType, "\".");
|
||||
}
|
||||
|
||||
function typeIncompatibleAnonSpreadMessage(parentType, fragType) {
|
||||
return 'Fragment cannot be spread here as objects of ' + "type \"".concat(parentType, "\" can never be of type \"").concat(fragType, "\".");
|
||||
}
|
||||
/**
|
||||
* Possible fragment spread
|
||||
*
|
||||
* A fragment spread is only valid if the type condition could ever possibly
|
||||
* be true: if there is a non-empty intersection of the possible parent types,
|
||||
* and possible types which pass the type condition.
|
||||
*/
|
||||
|
||||
|
||||
function PossibleFragmentSpreads(context) {
|
||||
return {
|
||||
InlineFragment: function InlineFragment(node) {
|
||||
var fragType = context.getType();
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if ((0, _definition.isCompositeType)(fragType) && (0, _definition.isCompositeType)(parentType) && !(0, _typeComparators.doTypesOverlap)(context.getSchema(), fragType, parentType)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(typeIncompatibleAnonSpreadMessage((0, _inspect.default)(parentType), (0, _inspect.default)(fragType)), [node]));
|
||||
}
|
||||
},
|
||||
FragmentSpread: function FragmentSpread(node) {
|
||||
var fragName = node.name.value;
|
||||
var fragType = getFragmentType(context, fragName);
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if (fragType && parentType && !(0, _typeComparators.doTypesOverlap)(context.getSchema(), fragType, parentType)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(typeIncompatibleSpreadMessage(fragName, (0, _inspect.default)(parentType), (0, _inspect.default)(fragType)), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFragmentType(context, name) {
|
||||
var frag = context.getFragment(name);
|
||||
|
||||
if (frag) {
|
||||
var type = (0, _typeFromAST.typeFromAST)(context.getSchema(), frag.typeCondition);
|
||||
|
||||
if ((0, _definition.isCompositeType)(type)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { doTypesOverlap } from '../../utilities/typeComparators';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
import { isCompositeType } from '../../type/definition';
|
||||
|
||||
export function typeIncompatibleSpreadMessage(
|
||||
fragName: string,
|
||||
parentType: string,
|
||||
fragType: string,
|
||||
): string {
|
||||
return (
|
||||
`Fragment "${fragName}" cannot be spread here as objects of ` +
|
||||
`type "${parentType}" can never be of type "${fragType}".`
|
||||
);
|
||||
}
|
||||
|
||||
export function typeIncompatibleAnonSpreadMessage(
|
||||
parentType: string,
|
||||
fragType: string,
|
||||
): string {
|
||||
return (
|
||||
'Fragment cannot be spread here as objects of ' +
|
||||
`type "${parentType}" can never be of type "${fragType}".`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible fragment spread
|
||||
*
|
||||
* A fragment spread is only valid if the type condition could ever possibly
|
||||
* be true: if there is a non-empty intersection of the possible parent types,
|
||||
* and possible types which pass the type condition.
|
||||
*/
|
||||
export function PossibleFragmentSpreads(
|
||||
context: ValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
InlineFragment(node) {
|
||||
const fragType = context.getType();
|
||||
const parentType = context.getParentType();
|
||||
if (
|
||||
isCompositeType(fragType) &&
|
||||
isCompositeType(parentType) &&
|
||||
!doTypesOverlap(context.getSchema(), fragType, parentType)
|
||||
) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
typeIncompatibleAnonSpreadMessage(
|
||||
inspect(parentType),
|
||||
inspect(fragType),
|
||||
),
|
||||
[node],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
FragmentSpread(node) {
|
||||
const fragName = node.name.value;
|
||||
const fragType = getFragmentType(context, fragName);
|
||||
const parentType = context.getParentType();
|
||||
if (
|
||||
fragType &&
|
||||
parentType &&
|
||||
!doTypesOverlap(context.getSchema(), fragType, parentType)
|
||||
) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
typeIncompatibleSpreadMessage(
|
||||
fragName,
|
||||
inspect(parentType),
|
||||
inspect(fragType),
|
||||
),
|
||||
[node],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getFragmentType(context, name) {
|
||||
const frag = context.getFragment(name);
|
||||
if (frag) {
|
||||
const type = typeFromAST(context.getSchema(), frag.typeCondition);
|
||||
if (isCompositeType(type)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { doTypesOverlap } from '../../utilities/typeComparators';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
import { isCompositeType } from '../../type/definition';
|
||||
export function typeIncompatibleSpreadMessage(fragName, parentType, fragType) {
|
||||
return "Fragment \"".concat(fragName, "\" cannot be spread here as objects of ") + "type \"".concat(parentType, "\" can never be of type \"").concat(fragType, "\".");
|
||||
}
|
||||
export function typeIncompatibleAnonSpreadMessage(parentType, fragType) {
|
||||
return 'Fragment cannot be spread here as objects of ' + "type \"".concat(parentType, "\" can never be of type \"").concat(fragType, "\".");
|
||||
}
|
||||
/**
|
||||
* Possible fragment spread
|
||||
*
|
||||
* A fragment spread is only valid if the type condition could ever possibly
|
||||
* be true: if there is a non-empty intersection of the possible parent types,
|
||||
* and possible types which pass the type condition.
|
||||
*/
|
||||
|
||||
export function PossibleFragmentSpreads(context) {
|
||||
return {
|
||||
InlineFragment: function InlineFragment(node) {
|
||||
var fragType = context.getType();
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if (isCompositeType(fragType) && isCompositeType(parentType) && !doTypesOverlap(context.getSchema(), fragType, parentType)) {
|
||||
context.reportError(new GraphQLError(typeIncompatibleAnonSpreadMessage(inspect(parentType), inspect(fragType)), [node]));
|
||||
}
|
||||
},
|
||||
FragmentSpread: function FragmentSpread(node) {
|
||||
var fragName = node.name.value;
|
||||
var fragType = getFragmentType(context, fragName);
|
||||
var parentType = context.getParentType();
|
||||
|
||||
if (fragType && parentType && !doTypesOverlap(context.getSchema(), fragType, parentType)) {
|
||||
context.reportError(new GraphQLError(typeIncompatibleSpreadMessage(fragName, inspect(parentType), inspect(fragType)), [node]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFragmentType(context, name) {
|
||||
var frag = context.getFragment(name);
|
||||
|
||||
if (frag) {
|
||||
var type = typeFromAST(context.getSchema(), frag.typeCondition);
|
||||
|
||||
if (isCompositeType(type)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.missingFieldArgMessage = missingFieldArgMessage;
|
||||
exports.missingDirectiveArgMessage = missingDirectiveArgMessage;
|
||||
exports.ProvidedRequiredArguments = ProvidedRequiredArguments;
|
||||
exports.ProvidedRequiredArgumentsOnDirectives = ProvidedRequiredArgumentsOnDirectives;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _keyMap = _interopRequireDefault(require("../../jsutils/keyMap"));
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _printer = require("../../language/printer");
|
||||
|
||||
var _directives = require("../../type/directives");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function missingFieldArgMessage(fieldName, argName, type) {
|
||||
return "Field \"".concat(fieldName, "\" argument \"").concat(argName, "\" of type ") + "\"".concat(type, "\" is required but not provided.");
|
||||
}
|
||||
|
||||
function missingDirectiveArgMessage(directiveName, argName, type) {
|
||||
return "Directive \"@".concat(directiveName, "\" argument \"").concat(argName, "\" of type ") + "\"".concat(type, "\" is required but not provided.");
|
||||
}
|
||||
/**
|
||||
* Provided required arguments
|
||||
*
|
||||
* A field or directive is only valid if all required (non-null without a
|
||||
* default value) field arguments have been provided.
|
||||
*/
|
||||
|
||||
|
||||
function ProvidedRequiredArguments(context) {
|
||||
return _objectSpread({}, ProvidedRequiredArgumentsOnDirectives(context), {
|
||||
Field: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave: function leave(fieldNode) {
|
||||
var fieldDef = context.getFieldDef();
|
||||
|
||||
if (!fieldDef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var argNodes = fieldNode.arguments || [];
|
||||
var argNodeMap = (0, _keyMap.default)(argNodes, function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = fieldDef.args[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var argDef = _step.value;
|
||||
var argNode = argNodeMap[argDef.name];
|
||||
|
||||
if (!argNode && (0, _definition.isRequiredArgument)(argDef)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(missingFieldArgMessage(fieldDef.name, argDef.name, (0, _inspect.default)(argDef.type)), [fieldNode]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} // @internal
|
||||
|
||||
|
||||
function ProvidedRequiredArgumentsOnDirectives(context) {
|
||||
var requiredArgsMap = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : _directives.specifiedDirectives;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = definedDirectives[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var directive = _step2.value;
|
||||
requiredArgsMap[directive.name] = (0, _keyMap.default)(directive.args.filter(_definition.isRequiredArgument), function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = astDefinitions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var def = _step3.value;
|
||||
|
||||
if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) {
|
||||
requiredArgsMap[def.name.value] = (0, _keyMap.default)(def.arguments ? def.arguments.filter(isRequiredArgumentNode) : [], function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave: function leave(directiveNode) {
|
||||
var directiveName = directiveNode.name.value;
|
||||
var requiredArgs = requiredArgsMap[directiveName];
|
||||
|
||||
if (requiredArgs) {
|
||||
var argNodes = directiveNode.arguments || [];
|
||||
var argNodeMap = (0, _keyMap.default)(argNodes, function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
|
||||
var _arr = Object.keys(requiredArgs);
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
var argName = _arr[_i];
|
||||
|
||||
if (!argNodeMap[argName]) {
|
||||
var argType = requiredArgs[argName].type;
|
||||
context.reportError(new _GraphQLError.GraphQLError(missingDirectiveArgMessage(directiveName, argName, (0, _definition.isType)(argType) ? (0, _inspect.default)(argType) : (0, _printer.print)(argType)), directiveNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isRequiredArgumentNode(arg) {
|
||||
return arg.type.kind === _kinds.Kind.NON_NULL_TYPE && arg.defaultValue == null;
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type {
|
||||
ValidationContext,
|
||||
SDLValidationContext,
|
||||
} from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import keyMap from '../../jsutils/keyMap';
|
||||
import { isType, isRequiredArgument } from '../../type/definition';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { print } from '../../language/printer';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
|
||||
export function missingFieldArgMessage(
|
||||
fieldName: string,
|
||||
argName: string,
|
||||
type: string,
|
||||
): string {
|
||||
return (
|
||||
`Field "${fieldName}" argument "${argName}" of type ` +
|
||||
`"${type}" is required but not provided.`
|
||||
);
|
||||
}
|
||||
|
||||
export function missingDirectiveArgMessage(
|
||||
directiveName: string,
|
||||
argName: string,
|
||||
type: string,
|
||||
): string {
|
||||
return (
|
||||
`Directive "@${directiveName}" argument "${argName}" of type ` +
|
||||
`"${type}" is required but not provided.`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provided required arguments
|
||||
*
|
||||
* A field or directive is only valid if all required (non-null without a
|
||||
* default value) field arguments have been provided.
|
||||
*/
|
||||
export function ProvidedRequiredArguments(
|
||||
context: ValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
...ProvidedRequiredArgumentsOnDirectives(context),
|
||||
Field: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave(fieldNode) {
|
||||
const fieldDef = context.getFieldDef();
|
||||
if (!fieldDef) {
|
||||
return false;
|
||||
}
|
||||
const argNodes = fieldNode.arguments || [];
|
||||
|
||||
const argNodeMap = keyMap(argNodes, arg => arg.name.value);
|
||||
for (const argDef of fieldDef.args) {
|
||||
const argNode = argNodeMap[argDef.name];
|
||||
if (!argNode && isRequiredArgument(argDef)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
missingFieldArgMessage(
|
||||
fieldDef.name,
|
||||
argDef.name,
|
||||
inspect(argDef.type),
|
||||
),
|
||||
[fieldNode],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function ProvidedRequiredArgumentsOnDirectives(
|
||||
context: ValidationContext | SDLValidationContext,
|
||||
): ASTVisitor {
|
||||
const requiredArgsMap = Object.create(null);
|
||||
|
||||
const schema = context.getSchema();
|
||||
const definedDirectives = schema
|
||||
? schema.getDirectives()
|
||||
: specifiedDirectives;
|
||||
for (const directive of definedDirectives) {
|
||||
requiredArgsMap[directive.name] = keyMap(
|
||||
directive.args.filter(isRequiredArgument),
|
||||
arg => arg.name,
|
||||
);
|
||||
}
|
||||
|
||||
const astDefinitions = context.getDocument().definitions;
|
||||
for (const def of astDefinitions) {
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
requiredArgsMap[def.name.value] = keyMap(
|
||||
def.arguments ? def.arguments.filter(isRequiredArgumentNode) : [],
|
||||
arg => arg.name.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave(directiveNode) {
|
||||
const directiveName = directiveNode.name.value;
|
||||
const requiredArgs = requiredArgsMap[directiveName];
|
||||
if (requiredArgs) {
|
||||
const argNodes = directiveNode.arguments || [];
|
||||
const argNodeMap = keyMap(argNodes, arg => arg.name.value);
|
||||
for (const argName of Object.keys(requiredArgs)) {
|
||||
if (!argNodeMap[argName]) {
|
||||
const argType = requiredArgs[argName].type;
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
missingDirectiveArgMessage(
|
||||
directiveName,
|
||||
argName,
|
||||
isType(argType) ? inspect(argType) : print(argType),
|
||||
),
|
||||
directiveNode,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function isRequiredArgumentNode(arg) {
|
||||
return arg.type.kind === Kind.NON_NULL_TYPE && arg.defaultValue == null;
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import keyMap from '../../jsutils/keyMap';
|
||||
import { isType, isRequiredArgument } from '../../type/definition';
|
||||
import { print } from '../../language/printer';
|
||||
import { specifiedDirectives } from '../../type/directives';
|
||||
export function missingFieldArgMessage(fieldName, argName, type) {
|
||||
return "Field \"".concat(fieldName, "\" argument \"").concat(argName, "\" of type ") + "\"".concat(type, "\" is required but not provided.");
|
||||
}
|
||||
export function missingDirectiveArgMessage(directiveName, argName, type) {
|
||||
return "Directive \"@".concat(directiveName, "\" argument \"").concat(argName, "\" of type ") + "\"".concat(type, "\" is required but not provided.");
|
||||
}
|
||||
/**
|
||||
* Provided required arguments
|
||||
*
|
||||
* A field or directive is only valid if all required (non-null without a
|
||||
* default value) field arguments have been provided.
|
||||
*/
|
||||
|
||||
export function ProvidedRequiredArguments(context) {
|
||||
return _objectSpread({}, ProvidedRequiredArgumentsOnDirectives(context), {
|
||||
Field: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave: function leave(fieldNode) {
|
||||
var fieldDef = context.getFieldDef();
|
||||
|
||||
if (!fieldDef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var argNodes = fieldNode.arguments || [];
|
||||
var argNodeMap = keyMap(argNodes, function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = fieldDef.args[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var argDef = _step.value;
|
||||
var argNode = argNodeMap[argDef.name];
|
||||
|
||||
if (!argNode && isRequiredArgument(argDef)) {
|
||||
context.reportError(new GraphQLError(missingFieldArgMessage(fieldDef.name, argDef.name, inspect(argDef.type)), [fieldNode]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} // @internal
|
||||
|
||||
export function ProvidedRequiredArgumentsOnDirectives(context) {
|
||||
var requiredArgsMap = Object.create(null);
|
||||
var schema = context.getSchema();
|
||||
var definedDirectives = schema ? schema.getDirectives() : specifiedDirectives;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = definedDirectives[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var directive = _step2.value;
|
||||
requiredArgsMap[directive.name] = keyMap(directive.args.filter(isRequiredArgument), function (arg) {
|
||||
return arg.name;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astDefinitions = context.getDocument().definitions;
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = astDefinitions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var def = _step3.value;
|
||||
|
||||
if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
||||
requiredArgsMap[def.name.value] = keyMap(def.arguments ? def.arguments.filter(isRequiredArgumentNode) : [], function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Directive: {
|
||||
// Validate on leave to allow for deeper errors to appear first.
|
||||
leave: function leave(directiveNode) {
|
||||
var directiveName = directiveNode.name.value;
|
||||
var requiredArgs = requiredArgsMap[directiveName];
|
||||
|
||||
if (requiredArgs) {
|
||||
var argNodes = directiveNode.arguments || [];
|
||||
var argNodeMap = keyMap(argNodes, function (arg) {
|
||||
return arg.name.value;
|
||||
});
|
||||
|
||||
var _arr = Object.keys(requiredArgs);
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
var argName = _arr[_i];
|
||||
|
||||
if (!argNodeMap[argName]) {
|
||||
var argType = requiredArgs[argName].type;
|
||||
context.reportError(new GraphQLError(missingDirectiveArgMessage(directiveName, argName, isType(argType) ? inspect(argType) : print(argType)), directiveNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isRequiredArgumentNode(arg) {
|
||||
return arg.type.kind === Kind.NON_NULL_TYPE && arg.defaultValue == null;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.noSubselectionAllowedMessage = noSubselectionAllowedMessage;
|
||||
exports.requiredSubselectionMessage = requiredSubselectionMessage;
|
||||
exports.ScalarLeafs = ScalarLeafs;
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function noSubselectionAllowedMessage(fieldName, type) {
|
||||
return "Field \"".concat(fieldName, "\" must not have a selection since ") + "type \"".concat(type, "\" has no subfields.");
|
||||
}
|
||||
|
||||
function requiredSubselectionMessage(fieldName, type) {
|
||||
return "Field \"".concat(fieldName, "\" of type \"").concat(type, "\" must have a ") + "selection of subfields. Did you mean \"".concat(fieldName, " { ... }\"?");
|
||||
}
|
||||
/**
|
||||
* Scalar leafs
|
||||
*
|
||||
* A GraphQL document is valid only if all leaf fields (fields without
|
||||
* sub selections) are of scalar or enum types.
|
||||
*/
|
||||
|
||||
|
||||
function ScalarLeafs(context) {
|
||||
return {
|
||||
Field: function Field(node) {
|
||||
var type = context.getType();
|
||||
var selectionSet = node.selectionSet;
|
||||
|
||||
if (type) {
|
||||
if ((0, _definition.isLeafType)((0, _definition.getNamedType)(type))) {
|
||||
if (selectionSet) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(noSubselectionAllowedMessage(node.name.value, (0, _inspect.default)(type)), [selectionSet]));
|
||||
}
|
||||
} else if (!selectionSet) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(requiredSubselectionMessage(node.name.value, (0, _inspect.default)(type)), [node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { FieldNode } from '../../language/ast';
|
||||
import { getNamedType, isLeafType } from '../../type/definition';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function noSubselectionAllowedMessage(
|
||||
fieldName: string,
|
||||
type: string,
|
||||
): string {
|
||||
return (
|
||||
`Field "${fieldName}" must not have a selection since ` +
|
||||
`type "${type}" has no subfields.`
|
||||
);
|
||||
}
|
||||
|
||||
export function requiredSubselectionMessage(
|
||||
fieldName: string,
|
||||
type: string,
|
||||
): string {
|
||||
return (
|
||||
`Field "${fieldName}" of type "${type}" must have a ` +
|
||||
`selection of subfields. Did you mean "${fieldName} { ... }"?`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scalar leafs
|
||||
*
|
||||
* A GraphQL document is valid only if all leaf fields (fields without
|
||||
* sub selections) are of scalar or enum types.
|
||||
*/
|
||||
export function ScalarLeafs(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
Field(node: FieldNode) {
|
||||
const type = context.getType();
|
||||
const selectionSet = node.selectionSet;
|
||||
if (type) {
|
||||
if (isLeafType(getNamedType(type))) {
|
||||
if (selectionSet) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
noSubselectionAllowedMessage(node.name.value, inspect(type)),
|
||||
[selectionSet],
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if (!selectionSet) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
requiredSubselectionMessage(node.name.value, inspect(type)),
|
||||
[node],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { getNamedType, isLeafType } from '../../type/definition';
|
||||
export function noSubselectionAllowedMessage(fieldName, type) {
|
||||
return "Field \"".concat(fieldName, "\" must not have a selection since ") + "type \"".concat(type, "\" has no subfields.");
|
||||
}
|
||||
export function requiredSubselectionMessage(fieldName, type) {
|
||||
return "Field \"".concat(fieldName, "\" of type \"").concat(type, "\" must have a ") + "selection of subfields. Did you mean \"".concat(fieldName, " { ... }\"?");
|
||||
}
|
||||
/**
|
||||
* Scalar leafs
|
||||
*
|
||||
* A GraphQL document is valid only if all leaf fields (fields without
|
||||
* sub selections) are of scalar or enum types.
|
||||
*/
|
||||
|
||||
export function ScalarLeafs(context) {
|
||||
return {
|
||||
Field: function Field(node) {
|
||||
var type = context.getType();
|
||||
var selectionSet = node.selectionSet;
|
||||
|
||||
if (type) {
|
||||
if (isLeafType(getNamedType(type))) {
|
||||
if (selectionSet) {
|
||||
context.reportError(new GraphQLError(noSubselectionAllowedMessage(node.name.value, inspect(type)), [selectionSet]));
|
||||
}
|
||||
} else if (!selectionSet) {
|
||||
context.reportError(new GraphQLError(requiredSubselectionMessage(node.name.value, inspect(type)), [node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.singleFieldOnlyMessage = singleFieldOnlyMessage;
|
||||
exports.SingleFieldSubscriptions = SingleFieldSubscriptions;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function singleFieldOnlyMessage(name) {
|
||||
return (name ? "Subscription \"".concat(name, "\" ") : 'Anonymous Subscription ') + 'must select only one top level field.';
|
||||
}
|
||||
/**
|
||||
* Subscriptions must only include one field.
|
||||
*
|
||||
* A GraphQL subscription is valid only if it contains a single root field.
|
||||
*/
|
||||
|
||||
|
||||
function SingleFieldSubscriptions(context) {
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
if (node.operation === 'subscription') {
|
||||
if (node.selectionSet.selections.length !== 1) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(singleFieldOnlyMessage(node.name && node.name.value), node.selectionSet.selections.slice(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { OperationDefinitionNode } from '../../language/ast';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function singleFieldOnlyMessage(name: ?string): string {
|
||||
return (
|
||||
(name ? `Subscription "${name}" ` : 'Anonymous Subscription ') +
|
||||
'must select only one top level field.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscriptions must only include one field.
|
||||
*
|
||||
* A GraphQL subscription is valid only if it contains a single root field.
|
||||
*/
|
||||
export function SingleFieldSubscriptions(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
OperationDefinition(node: OperationDefinitionNode) {
|
||||
if (node.operation === 'subscription') {
|
||||
if (node.selectionSet.selections.length !== 1) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
singleFieldOnlyMessage(node.name && node.name.value),
|
||||
node.selectionSet.selections.slice(1),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function singleFieldOnlyMessage(name) {
|
||||
return (name ? "Subscription \"".concat(name, "\" ") : 'Anonymous Subscription ') + 'must select only one top level field.';
|
||||
}
|
||||
/**
|
||||
* Subscriptions must only include one field.
|
||||
*
|
||||
* A GraphQL subscription is valid only if it contains a single root field.
|
||||
*/
|
||||
|
||||
export function SingleFieldSubscriptions(context) {
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
if (node.operation === 'subscription') {
|
||||
if (node.selectionSet.selections.length !== 1) {
|
||||
context.reportError(new GraphQLError(singleFieldOnlyMessage(node.name && node.name.value), node.selectionSet.selections.slice(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateArgMessage = duplicateArgMessage;
|
||||
exports.UniqueArgumentNames = UniqueArgumentNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateArgMessage(argName) {
|
||||
return "There can be only one argument named \"".concat(argName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique argument names
|
||||
*
|
||||
* A GraphQL field or directive is only valid if all supplied arguments are
|
||||
* uniquely named.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueArgumentNames(context) {
|
||||
var knownArgNames = Object.create(null);
|
||||
return {
|
||||
Field: function Field() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Directive: function Directive() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Argument: function Argument(node) {
|
||||
var argName = node.name.value;
|
||||
|
||||
if (knownArgNames[argName]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateArgMessage(argName), [knownArgNames[argName], node.name]));
|
||||
} else {
|
||||
knownArgNames[argName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateArgMessage(argName: string): string {
|
||||
return `There can be only one argument named "${argName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique argument names
|
||||
*
|
||||
* A GraphQL field or directive is only valid if all supplied arguments are
|
||||
* uniquely named.
|
||||
*/
|
||||
export function UniqueArgumentNames(context: ASTValidationContext): ASTVisitor {
|
||||
let knownArgNames = Object.create(null);
|
||||
return {
|
||||
Field() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Directive() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Argument(node) {
|
||||
const argName = node.name.value;
|
||||
if (knownArgNames[argName]) {
|
||||
context.reportError(
|
||||
new GraphQLError(duplicateArgMessage(argName), [
|
||||
knownArgNames[argName],
|
||||
node.name,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
knownArgNames[argName] = node.name;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateArgMessage(argName) {
|
||||
return "There can be only one argument named \"".concat(argName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique argument names
|
||||
*
|
||||
* A GraphQL field or directive is only valid if all supplied arguments are
|
||||
* uniquely named.
|
||||
*/
|
||||
|
||||
export function UniqueArgumentNames(context) {
|
||||
var knownArgNames = Object.create(null);
|
||||
return {
|
||||
Field: function Field() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Directive: function Directive() {
|
||||
knownArgNames = Object.create(null);
|
||||
},
|
||||
Argument: function Argument(node) {
|
||||
var argName = node.name.value;
|
||||
|
||||
if (knownArgNames[argName]) {
|
||||
context.reportError(new GraphQLError(duplicateArgMessage(argName), [knownArgNames[argName], node.name]));
|
||||
} else {
|
||||
knownArgNames[argName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateDirectiveMessage = duplicateDirectiveMessage;
|
||||
exports.UniqueDirectivesPerLocation = UniqueDirectivesPerLocation;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateDirectiveMessage(directiveName) {
|
||||
return "The directive \"".concat(directiveName, "\" can only be used once at ") + 'this location.';
|
||||
}
|
||||
/**
|
||||
* Unique directive names per location
|
||||
*
|
||||
* A GraphQL document is only valid if all directives at a given location
|
||||
* are uniquely named.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueDirectivesPerLocation(context) {
|
||||
return {
|
||||
// Many different AST nodes may contain directives. Rather than listing
|
||||
// them all, just listen for entering any node, and check to see if it
|
||||
// defines any directives.
|
||||
enter: function enter(node) {
|
||||
// Flow can't refine that node.directives will only contain directives,
|
||||
var directives = node.directives;
|
||||
|
||||
if (directives) {
|
||||
var knownDirectives = Object.create(null);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = directives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
var directiveName = directive.name.value;
|
||||
|
||||
if (knownDirectives[directiveName]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateDirectiveMessage(directiveName), [knownDirectives[directiveName], directive]));
|
||||
} else {
|
||||
knownDirectives[directiveName] = directive;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { DirectiveNode } from '../../language/ast';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateDirectiveMessage(directiveName: string): string {
|
||||
return (
|
||||
`The directive "${directiveName}" can only be used once at ` +
|
||||
'this location.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique directive names per location
|
||||
*
|
||||
* A GraphQL document is only valid if all directives at a given location
|
||||
* are uniquely named.
|
||||
*/
|
||||
export function UniqueDirectivesPerLocation(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
return {
|
||||
// Many different AST nodes may contain directives. Rather than listing
|
||||
// them all, just listen for entering any node, and check to see if it
|
||||
// defines any directives.
|
||||
enter(node) {
|
||||
// Flow can't refine that node.directives will only contain directives,
|
||||
// so we cast so the rest of the code is well typed.
|
||||
const directives: ?$ReadOnlyArray<DirectiveNode> = (node: any).directives;
|
||||
if (directives) {
|
||||
const knownDirectives = Object.create(null);
|
||||
for (const directive of directives) {
|
||||
const directiveName = directive.name.value;
|
||||
if (knownDirectives[directiveName]) {
|
||||
context.reportError(
|
||||
new GraphQLError(duplicateDirectiveMessage(directiveName), [
|
||||
knownDirectives[directiveName],
|
||||
directive,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
knownDirectives[directiveName] = directive;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateDirectiveMessage(directiveName) {
|
||||
return "The directive \"".concat(directiveName, "\" can only be used once at ") + 'this location.';
|
||||
}
|
||||
/**
|
||||
* Unique directive names per location
|
||||
*
|
||||
* A GraphQL document is only valid if all directives at a given location
|
||||
* are uniquely named.
|
||||
*/
|
||||
|
||||
export function UniqueDirectivesPerLocation(context) {
|
||||
return {
|
||||
// Many different AST nodes may contain directives. Rather than listing
|
||||
// them all, just listen for entering any node, and check to see if it
|
||||
// defines any directives.
|
||||
enter: function enter(node) {
|
||||
// Flow can't refine that node.directives will only contain directives,
|
||||
var directives = node.directives;
|
||||
|
||||
if (directives) {
|
||||
var knownDirectives = Object.create(null);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = directives[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var directive = _step.value;
|
||||
var directiveName = directive.name.value;
|
||||
|
||||
if (knownDirectives[directiveName]) {
|
||||
context.reportError(new GraphQLError(duplicateDirectiveMessage(directiveName), [knownDirectives[directiveName], directive]));
|
||||
} else {
|
||||
knownDirectives[directiveName] = directive;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateFragmentNameMessage = duplicateFragmentNameMessage;
|
||||
exports.UniqueFragmentNames = UniqueFragmentNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateFragmentNameMessage(fragName) {
|
||||
return "There can be only one fragment named \"".concat(fragName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined fragments have unique names.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueFragmentNames(context) {
|
||||
var knownFragmentNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
var fragmentName = node.name.value;
|
||||
|
||||
if (knownFragmentNames[fragmentName]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateFragmentNameMessage(fragmentName), [knownFragmentNames[fragmentName], node.name]));
|
||||
} else {
|
||||
knownFragmentNames[fragmentName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateFragmentNameMessage(fragName: string): string {
|
||||
return `There can be only one fragment named "${fragName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined fragments have unique names.
|
||||
*/
|
||||
export function UniqueFragmentNames(context: ASTValidationContext): ASTVisitor {
|
||||
const knownFragmentNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: () => false,
|
||||
FragmentDefinition(node) {
|
||||
const fragmentName = node.name.value;
|
||||
if (knownFragmentNames[fragmentName]) {
|
||||
context.reportError(
|
||||
new GraphQLError(duplicateFragmentNameMessage(fragmentName), [
|
||||
knownFragmentNames[fragmentName],
|
||||
node.name,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
knownFragmentNames[fragmentName] = node.name;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateFragmentNameMessage(fragName) {
|
||||
return "There can be only one fragment named \"".concat(fragName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique fragment names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined fragments have unique names.
|
||||
*/
|
||||
|
||||
export function UniqueFragmentNames(context) {
|
||||
var knownFragmentNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition(node) {
|
||||
var fragmentName = node.name.value;
|
||||
|
||||
if (knownFragmentNames[fragmentName]) {
|
||||
context.reportError(new GraphQLError(duplicateFragmentNameMessage(fragmentName), [knownFragmentNames[fragmentName], node.name]));
|
||||
} else {
|
||||
knownFragmentNames[fragmentName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateInputFieldMessage = duplicateInputFieldMessage;
|
||||
exports.UniqueInputFieldNames = UniqueInputFieldNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateInputFieldMessage(fieldName) {
|
||||
return "There can be only one input field named \"".concat(fieldName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique input field names
|
||||
*
|
||||
* A GraphQL input object value is only valid if all supplied fields are
|
||||
* uniquely named.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueInputFieldNames(context) {
|
||||
var knownNameStack = [];
|
||||
var knownNames = Object.create(null);
|
||||
return {
|
||||
ObjectValue: {
|
||||
enter: function enter() {
|
||||
knownNameStack.push(knownNames);
|
||||
knownNames = Object.create(null);
|
||||
},
|
||||
leave: function leave() {
|
||||
knownNames = knownNameStack.pop();
|
||||
}
|
||||
},
|
||||
ObjectField: function ObjectField(node) {
|
||||
var fieldName = node.name.value;
|
||||
|
||||
if (knownNames[fieldName]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateInputFieldMessage(fieldName), [knownNames[fieldName], node.name]));
|
||||
} else {
|
||||
knownNames[fieldName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateInputFieldMessage(fieldName: string): string {
|
||||
return `There can be only one input field named "${fieldName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique input field names
|
||||
*
|
||||
* A GraphQL input object value is only valid if all supplied fields are
|
||||
* uniquely named.
|
||||
*/
|
||||
export function UniqueInputFieldNames(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
const knownNameStack = [];
|
||||
let knownNames = Object.create(null);
|
||||
|
||||
return {
|
||||
ObjectValue: {
|
||||
enter() {
|
||||
knownNameStack.push(knownNames);
|
||||
knownNames = Object.create(null);
|
||||
},
|
||||
leave() {
|
||||
knownNames = knownNameStack.pop();
|
||||
},
|
||||
},
|
||||
ObjectField(node) {
|
||||
const fieldName = node.name.value;
|
||||
if (knownNames[fieldName]) {
|
||||
context.reportError(
|
||||
new GraphQLError(duplicateInputFieldMessage(fieldName), [
|
||||
knownNames[fieldName],
|
||||
node.name,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
knownNames[fieldName] = node.name;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateInputFieldMessage(fieldName) {
|
||||
return "There can be only one input field named \"".concat(fieldName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique input field names
|
||||
*
|
||||
* A GraphQL input object value is only valid if all supplied fields are
|
||||
* uniquely named.
|
||||
*/
|
||||
|
||||
export function UniqueInputFieldNames(context) {
|
||||
var knownNameStack = [];
|
||||
var knownNames = Object.create(null);
|
||||
return {
|
||||
ObjectValue: {
|
||||
enter: function enter() {
|
||||
knownNameStack.push(knownNames);
|
||||
knownNames = Object.create(null);
|
||||
},
|
||||
leave: function leave() {
|
||||
knownNames = knownNameStack.pop();
|
||||
}
|
||||
},
|
||||
ObjectField: function ObjectField(node) {
|
||||
var fieldName = node.name.value;
|
||||
|
||||
if (knownNames[fieldName]) {
|
||||
context.reportError(new GraphQLError(duplicateInputFieldMessage(fieldName), [knownNames[fieldName], node.name]));
|
||||
} else {
|
||||
knownNames[fieldName] = node.name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateOperationNameMessage = duplicateOperationNameMessage;
|
||||
exports.UniqueOperationNames = UniqueOperationNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateOperationNameMessage(operationName) {
|
||||
return "There can be only one operation named \"".concat(operationName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique operation names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined operations have unique names.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueOperationNames(context) {
|
||||
var knownOperationNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
var operationName = node.name;
|
||||
|
||||
if (operationName) {
|
||||
if (knownOperationNames[operationName.value]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateOperationNameMessage(operationName.value), [knownOperationNames[operationName.value], operationName]));
|
||||
} else {
|
||||
knownOperationNames[operationName.value] = operationName;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateOperationNameMessage(operationName: string): string {
|
||||
return `There can be only one operation named "${operationName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique operation names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined operations have unique names.
|
||||
*/
|
||||
export function UniqueOperationNames(
|
||||
context: ASTValidationContext,
|
||||
): ASTVisitor {
|
||||
const knownOperationNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition(node) {
|
||||
const operationName = node.name;
|
||||
if (operationName) {
|
||||
if (knownOperationNames[operationName.value]) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
duplicateOperationNameMessage(operationName.value),
|
||||
[knownOperationNames[operationName.value], operationName],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
knownOperationNames[operationName.value] = operationName;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: () => false,
|
||||
};
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateOperationNameMessage(operationName) {
|
||||
return "There can be only one operation named \"".concat(operationName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique operation names
|
||||
*
|
||||
* A GraphQL document is only valid if all defined operations have unique names.
|
||||
*/
|
||||
|
||||
export function UniqueOperationNames(context) {
|
||||
var knownOperationNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition(node) {
|
||||
var operationName = node.name;
|
||||
|
||||
if (operationName) {
|
||||
if (knownOperationNames[operationName.value]) {
|
||||
context.reportError(new GraphQLError(duplicateOperationNameMessage(operationName.value), [knownOperationNames[operationName.value], operationName]));
|
||||
} else {
|
||||
knownOperationNames[operationName.value] = operationName;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
FragmentDefinition: function FragmentDefinition() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.duplicateVariableMessage = duplicateVariableMessage;
|
||||
exports.UniqueVariableNames = UniqueVariableNames;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function duplicateVariableMessage(variableName) {
|
||||
return "There can be only one variable named \"".concat(variableName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique variable names
|
||||
*
|
||||
* A GraphQL operation is only valid if all its variables are uniquely named.
|
||||
*/
|
||||
|
||||
|
||||
function UniqueVariableNames(context) {
|
||||
var knownVariableNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
knownVariableNames = Object.create(null);
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
var variableName = node.variable.name.value;
|
||||
|
||||
if (knownVariableNames[variableName]) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(duplicateVariableMessage(variableName), [knownVariableNames[variableName], node.variable.name]));
|
||||
} else {
|
||||
knownVariableNames[variableName] = node.variable.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ASTValidationContext } from '../ValidationContext';
|
||||
import type { VariableDefinitionNode } from '../../language/ast';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
|
||||
export function duplicateVariableMessage(variableName: string): string {
|
||||
return `There can be only one variable named "${variableName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique variable names
|
||||
*
|
||||
* A GraphQL operation is only valid if all its variables are uniquely named.
|
||||
*/
|
||||
export function UniqueVariableNames(context: ASTValidationContext): ASTVisitor {
|
||||
let knownVariableNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition() {
|
||||
knownVariableNames = Object.create(null);
|
||||
},
|
||||
VariableDefinition(node: VariableDefinitionNode) {
|
||||
const variableName = node.variable.name.value;
|
||||
if (knownVariableNames[variableName]) {
|
||||
context.reportError(
|
||||
new GraphQLError(duplicateVariableMessage(variableName), [
|
||||
knownVariableNames[variableName],
|
||||
node.variable.name,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
knownVariableNames[variableName] = node.variable.name;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
export function duplicateVariableMessage(variableName) {
|
||||
return "There can be only one variable named \"".concat(variableName, "\".");
|
||||
}
|
||||
/**
|
||||
* Unique variable names
|
||||
*
|
||||
* A GraphQL operation is only valid if all its variables are uniquely named.
|
||||
*/
|
||||
|
||||
export function UniqueVariableNames(context) {
|
||||
var knownVariableNames = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: function OperationDefinition() {
|
||||
knownVariableNames = Object.create(null);
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
var variableName = node.variable.name.value;
|
||||
|
||||
if (knownVariableNames[variableName]) {
|
||||
context.reportError(new GraphQLError(duplicateVariableMessage(variableName), [knownVariableNames[variableName], node.variable.name]));
|
||||
} else {
|
||||
knownVariableNames[variableName] = node.variable.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.badValueMessage = badValueMessage;
|
||||
exports.requiredFieldMessage = requiredFieldMessage;
|
||||
exports.unknownFieldMessage = unknownFieldMessage;
|
||||
exports.ValuesOfCorrectType = ValuesOfCorrectType;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _printer = require("../../language/printer");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _isInvalid = _interopRequireDefault(require("../../jsutils/isInvalid"));
|
||||
|
||||
var _keyMap = _interopRequireDefault(require("../../jsutils/keyMap"));
|
||||
|
||||
var _orList = _interopRequireDefault(require("../../jsutils/orList"));
|
||||
|
||||
var _suggestionList = _interopRequireDefault(require("../../jsutils/suggestionList"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function badValueMessage(typeName, valueName, message) {
|
||||
return "Expected type ".concat(typeName, ", found ").concat(valueName) + (message ? "; ".concat(message) : '.');
|
||||
}
|
||||
|
||||
function requiredFieldMessage(typeName, fieldName, fieldTypeName) {
|
||||
return "Field ".concat(typeName, ".").concat(fieldName, " of required type ") + "".concat(fieldTypeName, " was not provided.");
|
||||
}
|
||||
|
||||
function unknownFieldMessage(typeName, fieldName, message) {
|
||||
return "Field \"".concat(fieldName, "\" is not defined by type ").concat(typeName) + (message ? "; ".concat(message) : '.');
|
||||
}
|
||||
/**
|
||||
* Value literals of correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all value literals are of the type
|
||||
* expected at their position.
|
||||
*/
|
||||
|
||||
|
||||
function ValuesOfCorrectType(context) {
|
||||
return {
|
||||
NullValue: function NullValue(node) {
|
||||
var type = context.getInputType();
|
||||
|
||||
if ((0, _definition.isNonNullType)(type)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(badValueMessage((0, _inspect.default)(type), (0, _printer.print)(node)), node));
|
||||
}
|
||||
},
|
||||
ListValue: function ListValue(node) {
|
||||
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||
// parent input type to check if it is a list.
|
||||
var type = (0, _definition.getNullableType)(context.getParentInputType());
|
||||
|
||||
if (!(0, _definition.isListType)(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
}
|
||||
},
|
||||
ObjectValue: function ObjectValue(node) {
|
||||
var type = (0, _definition.getNamedType)(context.getInputType());
|
||||
|
||||
if (!(0, _definition.isInputObjectType)(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
} // Ensure every required field exists.
|
||||
|
||||
|
||||
var inputFields = type.getFields();
|
||||
var fieldNodeMap = (0, _keyMap.default)(node.fields, function (field) {
|
||||
return field.name.value;
|
||||
});
|
||||
|
||||
var _arr = Object.keys(inputFields);
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
var fieldName = _arr[_i];
|
||||
var fieldDef = inputFields[fieldName];
|
||||
var fieldNode = fieldNodeMap[fieldName];
|
||||
|
||||
if (!fieldNode && (0, _definition.isRequiredInputField)(fieldDef)) {
|
||||
var typeStr = (0, _inspect.default)(fieldDef.type);
|
||||
context.reportError(new _GraphQLError.GraphQLError(requiredFieldMessage(type.name, fieldName, typeStr), node));
|
||||
}
|
||||
}
|
||||
},
|
||||
ObjectField: function ObjectField(node) {
|
||||
var parentType = (0, _definition.getNamedType)(context.getParentInputType());
|
||||
var fieldType = context.getInputType();
|
||||
|
||||
if (!fieldType && (0, _definition.isInputObjectType)(parentType)) {
|
||||
var suggestions = (0, _suggestionList.default)(node.name.value, Object.keys(parentType.getFields()));
|
||||
var didYouMean = suggestions.length !== 0 ? "Did you mean ".concat((0, _orList.default)(suggestions), "?") : undefined;
|
||||
context.reportError(new _GraphQLError.GraphQLError(unknownFieldMessage(parentType.name, node.name.value, didYouMean), node));
|
||||
}
|
||||
},
|
||||
EnumValue: function EnumValue(node) {
|
||||
var type = (0, _definition.getNamedType)(context.getInputType());
|
||||
|
||||
if (!(0, _definition.isEnumType)(type)) {
|
||||
isValidScalar(context, node);
|
||||
} else if (!type.getValue(node.value)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(badValueMessage(type.name, (0, _printer.print)(node), enumTypeSuggestion(type, node)), node));
|
||||
}
|
||||
},
|
||||
IntValue: function IntValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
FloatValue: function FloatValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
StringValue: function StringValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
BooleanValue: function BooleanValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Any value literal may be a valid representation of a Scalar, depending on
|
||||
* that scalar type.
|
||||
*/
|
||||
|
||||
|
||||
function isValidScalar(context, node) {
|
||||
// Report any error at the full type expected by the location.
|
||||
var locationType = context.getInputType();
|
||||
|
||||
if (!locationType) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = (0, _definition.getNamedType)(locationType);
|
||||
|
||||
if (!(0, _definition.isScalarType)(type)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(badValueMessage((0, _inspect.default)(locationType), (0, _printer.print)(node), enumTypeSuggestion(type, node)), node));
|
||||
return;
|
||||
} // Scalars determine if a literal value is valid via parseLiteral() which
|
||||
// may throw or return an invalid value to indicate failure.
|
||||
|
||||
|
||||
try {
|
||||
var parseResult = type.parseLiteral(node, undefined
|
||||
/* variables */
|
||||
);
|
||||
|
||||
if ((0, _isInvalid.default)(parseResult)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(badValueMessage((0, _inspect.default)(locationType), (0, _printer.print)(node)), node));
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure a reference to the original error is maintained.
|
||||
context.reportError(new _GraphQLError.GraphQLError(badValueMessage((0, _inspect.default)(locationType), (0, _printer.print)(node), error.message), node, undefined, undefined, undefined, error));
|
||||
}
|
||||
}
|
||||
|
||||
function enumTypeSuggestion(type, node) {
|
||||
if ((0, _definition.isEnumType)(type)) {
|
||||
var suggestions = (0, _suggestionList.default)((0, _printer.print)(node), type.getValues().map(function (value) {
|
||||
return value.name;
|
||||
}));
|
||||
|
||||
if (suggestions.length !== 0) {
|
||||
return "Did you mean the enum value ".concat((0, _orList.default)(suggestions), "?");
|
||||
}
|
||||
}
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { ValueNode } from '../../language/ast';
|
||||
import { print } from '../../language/printer';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import {
|
||||
isScalarType,
|
||||
isEnumType,
|
||||
isInputObjectType,
|
||||
isListType,
|
||||
isNonNullType,
|
||||
isRequiredInputField,
|
||||
getNullableType,
|
||||
getNamedType,
|
||||
} from '../../type/definition';
|
||||
import type { GraphQLType } from '../../type/definition';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import isInvalid from '../../jsutils/isInvalid';
|
||||
import keyMap from '../../jsutils/keyMap';
|
||||
import orList from '../../jsutils/orList';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
|
||||
export function badValueMessage(
|
||||
typeName: string,
|
||||
valueName: string,
|
||||
message?: string,
|
||||
): string {
|
||||
return (
|
||||
`Expected type ${typeName}, found ${valueName}` +
|
||||
(message ? `; ${message}` : '.')
|
||||
);
|
||||
}
|
||||
|
||||
export function requiredFieldMessage(
|
||||
typeName: string,
|
||||
fieldName: string,
|
||||
fieldTypeName: string,
|
||||
): string {
|
||||
return (
|
||||
`Field ${typeName}.${fieldName} of required type ` +
|
||||
`${fieldTypeName} was not provided.`
|
||||
);
|
||||
}
|
||||
|
||||
export function unknownFieldMessage(
|
||||
typeName: string,
|
||||
fieldName: string,
|
||||
message?: string,
|
||||
): string {
|
||||
return (
|
||||
`Field "${fieldName}" is not defined by type ${typeName}` +
|
||||
(message ? `; ${message}` : '.')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value literals of correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all value literals are of the type
|
||||
* expected at their position.
|
||||
*/
|
||||
export function ValuesOfCorrectType(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
NullValue(node) {
|
||||
const type = context.getInputType();
|
||||
if (isNonNullType(type)) {
|
||||
context.reportError(
|
||||
new GraphQLError(badValueMessage(inspect(type), print(node)), node),
|
||||
);
|
||||
}
|
||||
},
|
||||
ListValue(node) {
|
||||
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||
// parent input type to check if it is a list.
|
||||
const type = getNullableType(context.getParentInputType());
|
||||
if (!isListType(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
}
|
||||
},
|
||||
ObjectValue(node) {
|
||||
const type = getNamedType(context.getInputType());
|
||||
if (!isInputObjectType(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
}
|
||||
// Ensure every required field exists.
|
||||
const inputFields = type.getFields();
|
||||
const fieldNodeMap = keyMap(node.fields, field => field.name.value);
|
||||
for (const fieldName of Object.keys(inputFields)) {
|
||||
const fieldDef = inputFields[fieldName];
|
||||
const fieldNode = fieldNodeMap[fieldName];
|
||||
if (!fieldNode && isRequiredInputField(fieldDef)) {
|
||||
const typeStr = inspect(fieldDef.type);
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
requiredFieldMessage(type.name, fieldName, typeStr),
|
||||
node,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
ObjectField(node) {
|
||||
const parentType = getNamedType(context.getParentInputType());
|
||||
const fieldType = context.getInputType();
|
||||
if (!fieldType && isInputObjectType(parentType)) {
|
||||
const suggestions = suggestionList(
|
||||
node.name.value,
|
||||
Object.keys(parentType.getFields()),
|
||||
);
|
||||
const didYouMean =
|
||||
suggestions.length !== 0
|
||||
? `Did you mean ${orList(suggestions)}?`
|
||||
: undefined;
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
unknownFieldMessage(parentType.name, node.name.value, didYouMean),
|
||||
node,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
EnumValue(node) {
|
||||
const type = getNamedType(context.getInputType());
|
||||
if (!isEnumType(type)) {
|
||||
isValidScalar(context, node);
|
||||
} else if (!type.getValue(node.value)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
badValueMessage(
|
||||
type.name,
|
||||
print(node),
|
||||
enumTypeSuggestion(type, node),
|
||||
),
|
||||
node,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
IntValue: node => isValidScalar(context, node),
|
||||
FloatValue: node => isValidScalar(context, node),
|
||||
StringValue: node => isValidScalar(context, node),
|
||||
BooleanValue: node => isValidScalar(context, node),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Any value literal may be a valid representation of a Scalar, depending on
|
||||
* that scalar type.
|
||||
*/
|
||||
function isValidScalar(context: ValidationContext, node: ValueNode): void {
|
||||
// Report any error at the full type expected by the location.
|
||||
const locationType = context.getInputType();
|
||||
if (!locationType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = getNamedType(locationType);
|
||||
|
||||
if (!isScalarType(type)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
badValueMessage(
|
||||
inspect(locationType),
|
||||
print(node),
|
||||
enumTypeSuggestion(type, node),
|
||||
),
|
||||
node,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scalars determine if a literal value is valid via parseLiteral() which
|
||||
// may throw or return an invalid value to indicate failure.
|
||||
try {
|
||||
const parseResult = type.parseLiteral(node, undefined /* variables */);
|
||||
if (isInvalid(parseResult)) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
badValueMessage(inspect(locationType), print(node)),
|
||||
node,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure a reference to the original error is maintained.
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
badValueMessage(inspect(locationType), print(node), error.message),
|
||||
node,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function enumTypeSuggestion(type: GraphQLType, node: ValueNode): string | void {
|
||||
if (isEnumType(type)) {
|
||||
const suggestions = suggestionList(
|
||||
print(node),
|
||||
type.getValues().map(value => value.name),
|
||||
);
|
||||
if (suggestions.length !== 0) {
|
||||
return `Did you mean the enum value ${orList(suggestions)}?`;
|
||||
}
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { print } from '../../language/printer';
|
||||
import { isScalarType, isEnumType, isInputObjectType, isListType, isNonNullType, isRequiredInputField, getNullableType, getNamedType } from '../../type/definition';
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import isInvalid from '../../jsutils/isInvalid';
|
||||
import keyMap from '../../jsutils/keyMap';
|
||||
import orList from '../../jsutils/orList';
|
||||
import suggestionList from '../../jsutils/suggestionList';
|
||||
export function badValueMessage(typeName, valueName, message) {
|
||||
return "Expected type ".concat(typeName, ", found ").concat(valueName) + (message ? "; ".concat(message) : '.');
|
||||
}
|
||||
export function requiredFieldMessage(typeName, fieldName, fieldTypeName) {
|
||||
return "Field ".concat(typeName, ".").concat(fieldName, " of required type ") + "".concat(fieldTypeName, " was not provided.");
|
||||
}
|
||||
export function unknownFieldMessage(typeName, fieldName, message) {
|
||||
return "Field \"".concat(fieldName, "\" is not defined by type ").concat(typeName) + (message ? "; ".concat(message) : '.');
|
||||
}
|
||||
/**
|
||||
* Value literals of correct type
|
||||
*
|
||||
* A GraphQL document is only valid if all value literals are of the type
|
||||
* expected at their position.
|
||||
*/
|
||||
|
||||
export function ValuesOfCorrectType(context) {
|
||||
return {
|
||||
NullValue: function NullValue(node) {
|
||||
var type = context.getInputType();
|
||||
|
||||
if (isNonNullType(type)) {
|
||||
context.reportError(new GraphQLError(badValueMessage(inspect(type), print(node)), node));
|
||||
}
|
||||
},
|
||||
ListValue: function ListValue(node) {
|
||||
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||
// parent input type to check if it is a list.
|
||||
var type = getNullableType(context.getParentInputType());
|
||||
|
||||
if (!isListType(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
}
|
||||
},
|
||||
ObjectValue: function ObjectValue(node) {
|
||||
var type = getNamedType(context.getInputType());
|
||||
|
||||
if (!isInputObjectType(type)) {
|
||||
isValidScalar(context, node);
|
||||
return false; // Don't traverse further.
|
||||
} // Ensure every required field exists.
|
||||
|
||||
|
||||
var inputFields = type.getFields();
|
||||
var fieldNodeMap = keyMap(node.fields, function (field) {
|
||||
return field.name.value;
|
||||
});
|
||||
|
||||
var _arr = Object.keys(inputFields);
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
var fieldName = _arr[_i];
|
||||
var fieldDef = inputFields[fieldName];
|
||||
var fieldNode = fieldNodeMap[fieldName];
|
||||
|
||||
if (!fieldNode && isRequiredInputField(fieldDef)) {
|
||||
var typeStr = inspect(fieldDef.type);
|
||||
context.reportError(new GraphQLError(requiredFieldMessage(type.name, fieldName, typeStr), node));
|
||||
}
|
||||
}
|
||||
},
|
||||
ObjectField: function ObjectField(node) {
|
||||
var parentType = getNamedType(context.getParentInputType());
|
||||
var fieldType = context.getInputType();
|
||||
|
||||
if (!fieldType && isInputObjectType(parentType)) {
|
||||
var suggestions = suggestionList(node.name.value, Object.keys(parentType.getFields()));
|
||||
var didYouMean = suggestions.length !== 0 ? "Did you mean ".concat(orList(suggestions), "?") : undefined;
|
||||
context.reportError(new GraphQLError(unknownFieldMessage(parentType.name, node.name.value, didYouMean), node));
|
||||
}
|
||||
},
|
||||
EnumValue: function EnumValue(node) {
|
||||
var type = getNamedType(context.getInputType());
|
||||
|
||||
if (!isEnumType(type)) {
|
||||
isValidScalar(context, node);
|
||||
} else if (!type.getValue(node.value)) {
|
||||
context.reportError(new GraphQLError(badValueMessage(type.name, print(node), enumTypeSuggestion(type, node)), node));
|
||||
}
|
||||
},
|
||||
IntValue: function IntValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
FloatValue: function FloatValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
StringValue: function StringValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
},
|
||||
BooleanValue: function BooleanValue(node) {
|
||||
return isValidScalar(context, node);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Any value literal may be a valid representation of a Scalar, depending on
|
||||
* that scalar type.
|
||||
*/
|
||||
|
||||
function isValidScalar(context, node) {
|
||||
// Report any error at the full type expected by the location.
|
||||
var locationType = context.getInputType();
|
||||
|
||||
if (!locationType) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = getNamedType(locationType);
|
||||
|
||||
if (!isScalarType(type)) {
|
||||
context.reportError(new GraphQLError(badValueMessage(inspect(locationType), print(node), enumTypeSuggestion(type, node)), node));
|
||||
return;
|
||||
} // Scalars determine if a literal value is valid via parseLiteral() which
|
||||
// may throw or return an invalid value to indicate failure.
|
||||
|
||||
|
||||
try {
|
||||
var parseResult = type.parseLiteral(node, undefined
|
||||
/* variables */
|
||||
);
|
||||
|
||||
if (isInvalid(parseResult)) {
|
||||
context.reportError(new GraphQLError(badValueMessage(inspect(locationType), print(node)), node));
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure a reference to the original error is maintained.
|
||||
context.reportError(new GraphQLError(badValueMessage(inspect(locationType), print(node), error.message), node, undefined, undefined, undefined, error));
|
||||
}
|
||||
}
|
||||
|
||||
function enumTypeSuggestion(type, node) {
|
||||
if (isEnumType(type)) {
|
||||
var suggestions = suggestionList(print(node), type.getValues().map(function (value) {
|
||||
return value.name;
|
||||
}));
|
||||
|
||||
if (suggestions.length !== 0) {
|
||||
return "Did you mean the enum value ".concat(orList(suggestions), "?");
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.nonInputTypeOnVarMessage = nonInputTypeOnVarMessage;
|
||||
exports.VariablesAreInputTypes = VariablesAreInputTypes;
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _printer = require("../../language/printer");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _typeFromAST = require("../../utilities/typeFromAST");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function nonInputTypeOnVarMessage(variableName, typeName) {
|
||||
return "Variable \"$".concat(variableName, "\" cannot be non-input type \"").concat(typeName, "\".");
|
||||
}
|
||||
/**
|
||||
* Variables are input types
|
||||
*
|
||||
* A GraphQL operation is only valid if all the variables it defines are of
|
||||
* input types (scalar, enum, or input object).
|
||||
*/
|
||||
|
||||
|
||||
function VariablesAreInputTypes(context) {
|
||||
return {
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
var type = (0, _typeFromAST.typeFromAST)(context.getSchema(), node.type); // If the variable type is not an input type, return an error.
|
||||
|
||||
if (type && !(0, _definition.isInputType)(type)) {
|
||||
var variableName = node.variable.name.value;
|
||||
context.reportError(new _GraphQLError.GraphQLError(nonInputTypeOnVarMessage(variableName, (0, _printer.print)(node.type)), [node.type]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import type { VariableDefinitionNode } from '../../language/ast';
|
||||
import { print } from '../../language/printer';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { isInputType } from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
|
||||
export function nonInputTypeOnVarMessage(
|
||||
variableName: string,
|
||||
typeName: string,
|
||||
): string {
|
||||
return `Variable "$${variableName}" cannot be non-input type "${typeName}".`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables are input types
|
||||
*
|
||||
* A GraphQL operation is only valid if all the variables it defines are of
|
||||
* input types (scalar, enum, or input object).
|
||||
*/
|
||||
export function VariablesAreInputTypes(context: ValidationContext): ASTVisitor {
|
||||
return {
|
||||
VariableDefinition(node: VariableDefinitionNode): ?GraphQLError {
|
||||
const type = typeFromAST(context.getSchema(), node.type);
|
||||
|
||||
// If the variable type is not an input type, return an error.
|
||||
if (type && !isInputType(type)) {
|
||||
const variableName = node.variable.name.value;
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
nonInputTypeOnVarMessage(variableName, print(node.type)),
|
||||
[node.type],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { print } from '../../language/printer';
|
||||
import { isInputType } from '../../type/definition';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
export function nonInputTypeOnVarMessage(variableName, typeName) {
|
||||
return "Variable \"$".concat(variableName, "\" cannot be non-input type \"").concat(typeName, "\".");
|
||||
}
|
||||
/**
|
||||
* Variables are input types
|
||||
*
|
||||
* A GraphQL operation is only valid if all the variables it defines are of
|
||||
* input types (scalar, enum, or input object).
|
||||
*/
|
||||
|
||||
export function VariablesAreInputTypes(context) {
|
||||
return {
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
var type = typeFromAST(context.getSchema(), node.type); // If the variable type is not an input type, return an error.
|
||||
|
||||
if (type && !isInputType(type)) {
|
||||
var variableName = node.variable.name.value;
|
||||
context.reportError(new GraphQLError(nonInputTypeOnVarMessage(variableName, print(node.type)), [node.type]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.badVarPosMessage = badVarPosMessage;
|
||||
exports.VariablesInAllowedPosition = VariablesInAllowedPosition;
|
||||
|
||||
var _inspect = _interopRequireDefault(require("../../jsutils/inspect"));
|
||||
|
||||
var _GraphQLError = require("../../error/GraphQLError");
|
||||
|
||||
var _kinds = require("../../language/kinds");
|
||||
|
||||
var _definition = require("../../type/definition");
|
||||
|
||||
var _typeComparators = require("../../utilities/typeComparators");
|
||||
|
||||
var _typeFromAST = require("../../utilities/typeFromAST");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
function badVarPosMessage(varName, varType, expectedType) {
|
||||
return "Variable \"$".concat(varName, "\" of type \"").concat(varType, "\" used in ") + "position expecting type \"".concat(expectedType, "\".");
|
||||
}
|
||||
/**
|
||||
* Variables passed to field arguments conform to type
|
||||
*/
|
||||
|
||||
|
||||
function VariablesInAllowedPosition(context) {
|
||||
var varDefMap = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
varDefMap = Object.create(null);
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node,
|
||||
type = _ref2.type,
|
||||
defaultValue = _ref2.defaultValue;
|
||||
var varName = node.name.value;
|
||||
var varDef = varDefMap[varName];
|
||||
|
||||
if (varDef && type) {
|
||||
// A var type is allowed if it is the same or more strict (e.g. is
|
||||
// a subtype of) than the expected type. It can be more strict if
|
||||
// the variable type is non-null when the expected type is nullable.
|
||||
// If both are list types, the variable item type can be more strict
|
||||
// than the expected item type (contravariant).
|
||||
var schema = context.getSchema();
|
||||
var varType = (0, _typeFromAST.typeFromAST)(schema, varDef.type);
|
||||
|
||||
if (varType && !allowedVariableUsage(schema, varType, varDef.defaultValue, type, defaultValue)) {
|
||||
context.reportError(new _GraphQLError.GraphQLError(badVarPosMessage(varName, (0, _inspect.default)(varType), (0, _inspect.default)(type)), [varDef, node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
varDefMap[node.variable.name.value] = node;
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Returns true if the variable is allowed in the location it was found,
|
||||
* which includes considering if default values exist for either the variable
|
||||
* or the location at which it is located.
|
||||
*/
|
||||
|
||||
|
||||
function allowedVariableUsage(schema, varType, varDefaultValue, locationType, locationDefaultValue) {
|
||||
if ((0, _definition.isNonNullType)(locationType) && !(0, _definition.isNonNullType)(varType)) {
|
||||
var hasNonNullVariableDefaultValue = varDefaultValue && varDefaultValue.kind !== _kinds.Kind.NULL;
|
||||
var hasLocationDefaultValue = locationDefaultValue !== undefined;
|
||||
|
||||
if (!hasNonNullVariableDefaultValue && !hasLocationDefaultValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var nullableLocationType = locationType.ofType;
|
||||
return (0, _typeComparators.isTypeSubTypeOf)(schema, varType, nullableLocationType);
|
||||
}
|
||||
|
||||
return (0, _typeComparators.isTypeSubTypeOf)(schema, varType, locationType);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import type { ValidationContext } from '../ValidationContext';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import type { ValueNode } from '../../language/ast';
|
||||
import type { ASTVisitor } from '../../language/visitor';
|
||||
import { isNonNullType } from '../../type/definition';
|
||||
import { isTypeSubTypeOf } from '../../utilities/typeComparators';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
import type { GraphQLType } from '../../type/definition';
|
||||
import type { GraphQLSchema } from '../../type/schema';
|
||||
|
||||
export function badVarPosMessage(
|
||||
varName: string,
|
||||
varType: string,
|
||||
expectedType: string,
|
||||
): string {
|
||||
return (
|
||||
`Variable "$${varName}" of type "${varType}" used in ` +
|
||||
`position expecting type "${expectedType}".`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables passed to field arguments conform to type
|
||||
*/
|
||||
export function VariablesInAllowedPosition(
|
||||
context: ValidationContext,
|
||||
): ASTVisitor {
|
||||
let varDefMap = Object.create(null);
|
||||
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter() {
|
||||
varDefMap = Object.create(null);
|
||||
},
|
||||
leave(operation) {
|
||||
const usages = context.getRecursiveVariableUsages(operation);
|
||||
|
||||
for (const { node, type, defaultValue } of usages) {
|
||||
const varName = node.name.value;
|
||||
const varDef = varDefMap[varName];
|
||||
if (varDef && type) {
|
||||
// A var type is allowed if it is the same or more strict (e.g. is
|
||||
// a subtype of) than the expected type. It can be more strict if
|
||||
// the variable type is non-null when the expected type is nullable.
|
||||
// If both are list types, the variable item type can be more strict
|
||||
// than the expected item type (contravariant).
|
||||
const schema = context.getSchema();
|
||||
const varType = typeFromAST(schema, varDef.type);
|
||||
if (
|
||||
varType &&
|
||||
!allowedVariableUsage(
|
||||
schema,
|
||||
varType,
|
||||
varDef.defaultValue,
|
||||
type,
|
||||
defaultValue,
|
||||
)
|
||||
) {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
badVarPosMessage(varName, inspect(varType), inspect(type)),
|
||||
[varDef, node],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
VariableDefinition(node) {
|
||||
varDefMap[node.variable.name.value] = node;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the variable is allowed in the location it was found,
|
||||
* which includes considering if default values exist for either the variable
|
||||
* or the location at which it is located.
|
||||
*/
|
||||
function allowedVariableUsage(
|
||||
schema: GraphQLSchema,
|
||||
varType: GraphQLType,
|
||||
varDefaultValue: ?ValueNode,
|
||||
locationType: GraphQLType,
|
||||
locationDefaultValue: ?mixed,
|
||||
): boolean {
|
||||
if (isNonNullType(locationType) && !isNonNullType(varType)) {
|
||||
const hasNonNullVariableDefaultValue =
|
||||
varDefaultValue && varDefaultValue.kind !== Kind.NULL;
|
||||
const hasLocationDefaultValue = locationDefaultValue !== undefined;
|
||||
if (!hasNonNullVariableDefaultValue && !hasLocationDefaultValue) {
|
||||
return false;
|
||||
}
|
||||
const nullableLocationType = locationType.ofType;
|
||||
return isTypeSubTypeOf(schema, varType, nullableLocationType);
|
||||
}
|
||||
return isTypeSubTypeOf(schema, varType, locationType);
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import inspect from '../../jsutils/inspect';
|
||||
import { GraphQLError } from '../../error/GraphQLError';
|
||||
import { Kind } from '../../language/kinds';
|
||||
import { isNonNullType } from '../../type/definition';
|
||||
import { isTypeSubTypeOf } from '../../utilities/typeComparators';
|
||||
import { typeFromAST } from '../../utilities/typeFromAST';
|
||||
export function badVarPosMessage(varName, varType, expectedType) {
|
||||
return "Variable \"$".concat(varName, "\" of type \"").concat(varType, "\" used in ") + "position expecting type \"".concat(expectedType, "\".");
|
||||
}
|
||||
/**
|
||||
* Variables passed to field arguments conform to type
|
||||
*/
|
||||
|
||||
export function VariablesInAllowedPosition(context) {
|
||||
var varDefMap = Object.create(null);
|
||||
return {
|
||||
OperationDefinition: {
|
||||
enter: function enter() {
|
||||
varDefMap = Object.create(null);
|
||||
},
|
||||
leave: function leave(operation) {
|
||||
var usages = context.getRecursiveVariableUsages(operation);
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = usages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var _ref2 = _step.value;
|
||||
var node = _ref2.node,
|
||||
type = _ref2.type,
|
||||
defaultValue = _ref2.defaultValue;
|
||||
var varName = node.name.value;
|
||||
var varDef = varDefMap[varName];
|
||||
|
||||
if (varDef && type) {
|
||||
// A var type is allowed if it is the same or more strict (e.g. is
|
||||
// a subtype of) than the expected type. It can be more strict if
|
||||
// the variable type is non-null when the expected type is nullable.
|
||||
// If both are list types, the variable item type can be more strict
|
||||
// than the expected item type (contravariant).
|
||||
var schema = context.getSchema();
|
||||
var varType = typeFromAST(schema, varDef.type);
|
||||
|
||||
if (varType && !allowedVariableUsage(schema, varType, varDef.defaultValue, type, defaultValue)) {
|
||||
context.reportError(new GraphQLError(badVarPosMessage(varName, inspect(varType), inspect(type)), [varDef, node]));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDefinition: function VariableDefinition(node) {
|
||||
varDefMap[node.variable.name.value] = node;
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Returns true if the variable is allowed in the location it was found,
|
||||
* which includes considering if default values exist for either the variable
|
||||
* or the location at which it is located.
|
||||
*/
|
||||
|
||||
function allowedVariableUsage(schema, varType, varDefaultValue, locationType, locationDefaultValue) {
|
||||
if (isNonNullType(locationType) && !isNonNullType(varType)) {
|
||||
var hasNonNullVariableDefaultValue = varDefaultValue && varDefaultValue.kind !== Kind.NULL;
|
||||
var hasLocationDefaultValue = locationDefaultValue !== undefined;
|
||||
|
||||
if (!hasNonNullVariableDefaultValue && !hasLocationDefaultValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var nullableLocationType = locationType.ofType;
|
||||
return isTypeSubTypeOf(schema, varType, nullableLocationType);
|
||||
}
|
||||
|
||||
return isTypeSubTypeOf(schema, varType, locationType);
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.specifiedSDLRules = exports.specifiedRules = void 0;
|
||||
|
||||
var _ExecutableDefinitions = require("./rules/ExecutableDefinitions");
|
||||
|
||||
var _UniqueOperationNames = require("./rules/UniqueOperationNames");
|
||||
|
||||
var _LoneAnonymousOperation = require("./rules/LoneAnonymousOperation");
|
||||
|
||||
var _SingleFieldSubscriptions = require("./rules/SingleFieldSubscriptions");
|
||||
|
||||
var _KnownTypeNames = require("./rules/KnownTypeNames");
|
||||
|
||||
var _FragmentsOnCompositeTypes = require("./rules/FragmentsOnCompositeTypes");
|
||||
|
||||
var _VariablesAreInputTypes = require("./rules/VariablesAreInputTypes");
|
||||
|
||||
var _ScalarLeafs = require("./rules/ScalarLeafs");
|
||||
|
||||
var _FieldsOnCorrectType = require("./rules/FieldsOnCorrectType");
|
||||
|
||||
var _UniqueFragmentNames = require("./rules/UniqueFragmentNames");
|
||||
|
||||
var _KnownFragmentNames = require("./rules/KnownFragmentNames");
|
||||
|
||||
var _NoUnusedFragments = require("./rules/NoUnusedFragments");
|
||||
|
||||
var _PossibleFragmentSpreads = require("./rules/PossibleFragmentSpreads");
|
||||
|
||||
var _NoFragmentCycles = require("./rules/NoFragmentCycles");
|
||||
|
||||
var _UniqueVariableNames = require("./rules/UniqueVariableNames");
|
||||
|
||||
var _NoUndefinedVariables = require("./rules/NoUndefinedVariables");
|
||||
|
||||
var _NoUnusedVariables = require("./rules/NoUnusedVariables");
|
||||
|
||||
var _KnownDirectives = require("./rules/KnownDirectives");
|
||||
|
||||
var _UniqueDirectivesPerLocation = require("./rules/UniqueDirectivesPerLocation");
|
||||
|
||||
var _KnownArgumentNames = require("./rules/KnownArgumentNames");
|
||||
|
||||
var _UniqueArgumentNames = require("./rules/UniqueArgumentNames");
|
||||
|
||||
var _ValuesOfCorrectType = require("./rules/ValuesOfCorrectType");
|
||||
|
||||
var _ProvidedRequiredArguments = require("./rules/ProvidedRequiredArguments");
|
||||
|
||||
var _VariablesInAllowedPosition = require("./rules/VariablesInAllowedPosition");
|
||||
|
||||
var _OverlappingFieldsCanBeMerged = require("./rules/OverlappingFieldsCanBeMerged");
|
||||
|
||||
var _UniqueInputFieldNames = require("./rules/UniqueInputFieldNames");
|
||||
|
||||
var _LoneSchemaDefinition = require("./rules/LoneSchemaDefinition");
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
// Spec Section: "Executable Definitions"
|
||||
// Spec Section: "Operation Name Uniqueness"
|
||||
// Spec Section: "Lone Anonymous Operation"
|
||||
// Spec Section: "Subscriptions with Single Root Field"
|
||||
// Spec Section: "Fragment Spread Type Existence"
|
||||
// Spec Section: "Fragments on Composite Types"
|
||||
// Spec Section: "Variables are Input Types"
|
||||
// Spec Section: "Leaf Field Selections"
|
||||
// Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
|
||||
// Spec Section: "Fragment Name Uniqueness"
|
||||
// Spec Section: "Fragment spread target defined"
|
||||
// Spec Section: "Fragments must be used"
|
||||
// Spec Section: "Fragment spread is possible"
|
||||
// Spec Section: "Fragments must not form cycles"
|
||||
// Spec Section: "Variable Uniqueness"
|
||||
// Spec Section: "All Variable Used Defined"
|
||||
// Spec Section: "All Variables Used"
|
||||
// Spec Section: "Directives Are Defined"
|
||||
// Spec Section: "Directives Are Unique Per Location"
|
||||
// Spec Section: "Argument Names"
|
||||
// Spec Section: "Argument Uniqueness"
|
||||
// Spec Section: "Value Type Correctness"
|
||||
// Spec Section: "Argument Optionality"
|
||||
// Spec Section: "All Variable Usages Are Allowed"
|
||||
// Spec Section: "Field Selection Merging"
|
||||
// Spec Section: "Input Object Field Uniqueness"
|
||||
|
||||
/**
|
||||
* This set includes all validation rules defined by the GraphQL spec.
|
||||
*
|
||||
* The order of the rules in this list has been adjusted to lead to the
|
||||
* most clear output when encountering multiple validation errors.
|
||||
*/
|
||||
var specifiedRules = [_ExecutableDefinitions.ExecutableDefinitions, _UniqueOperationNames.UniqueOperationNames, _LoneAnonymousOperation.LoneAnonymousOperation, _SingleFieldSubscriptions.SingleFieldSubscriptions, _KnownTypeNames.KnownTypeNames, _FragmentsOnCompositeTypes.FragmentsOnCompositeTypes, _VariablesAreInputTypes.VariablesAreInputTypes, _ScalarLeafs.ScalarLeafs, _FieldsOnCorrectType.FieldsOnCorrectType, _UniqueFragmentNames.UniqueFragmentNames, _KnownFragmentNames.KnownFragmentNames, _NoUnusedFragments.NoUnusedFragments, _PossibleFragmentSpreads.PossibleFragmentSpreads, _NoFragmentCycles.NoFragmentCycles, _UniqueVariableNames.UniqueVariableNames, _NoUndefinedVariables.NoUndefinedVariables, _NoUnusedVariables.NoUnusedVariables, _KnownDirectives.KnownDirectives, _UniqueDirectivesPerLocation.UniqueDirectivesPerLocation, _KnownArgumentNames.KnownArgumentNames, _UniqueArgumentNames.UniqueArgumentNames, _ValuesOfCorrectType.ValuesOfCorrectType, _ProvidedRequiredArguments.ProvidedRequiredArguments, _VariablesInAllowedPosition.VariablesInAllowedPosition, _OverlappingFieldsCanBeMerged.OverlappingFieldsCanBeMerged, _UniqueInputFieldNames.UniqueInputFieldNames];
|
||||
exports.specifiedRules = specifiedRules;
|
||||
// @internal
|
||||
var specifiedSDLRules = [_LoneSchemaDefinition.LoneSchemaDefinition, _KnownDirectives.KnownDirectives, _UniqueDirectivesPerLocation.UniqueDirectivesPerLocation, _KnownArgumentNames.KnownArgumentNamesOnDirectives, _UniqueArgumentNames.UniqueArgumentNames, _UniqueInputFieldNames.UniqueInputFieldNames, _ProvidedRequiredArguments.ProvidedRequiredArgumentsOnDirectives];
|
||||
exports.specifiedSDLRules = specifiedSDLRules;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import type { ValidationRule, SDLValidationRule } from './ValidationContext';
|
||||
|
||||
// Spec Section: "Executable Definitions"
|
||||
import { ExecutableDefinitions } from './rules/ExecutableDefinitions';
|
||||
|
||||
// Spec Section: "Operation Name Uniqueness"
|
||||
import { UniqueOperationNames } from './rules/UniqueOperationNames';
|
||||
|
||||
// Spec Section: "Lone Anonymous Operation"
|
||||
import { LoneAnonymousOperation } from './rules/LoneAnonymousOperation';
|
||||
|
||||
// Spec Section: "Subscriptions with Single Root Field"
|
||||
import { SingleFieldSubscriptions } from './rules/SingleFieldSubscriptions';
|
||||
|
||||
// Spec Section: "Fragment Spread Type Existence"
|
||||
import { KnownTypeNames } from './rules/KnownTypeNames';
|
||||
|
||||
// Spec Section: "Fragments on Composite Types"
|
||||
import { FragmentsOnCompositeTypes } from './rules/FragmentsOnCompositeTypes';
|
||||
|
||||
// Spec Section: "Variables are Input Types"
|
||||
import { VariablesAreInputTypes } from './rules/VariablesAreInputTypes';
|
||||
|
||||
// Spec Section: "Leaf Field Selections"
|
||||
import { ScalarLeafs } from './rules/ScalarLeafs';
|
||||
|
||||
// Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
|
||||
import { FieldsOnCorrectType } from './rules/FieldsOnCorrectType';
|
||||
|
||||
// Spec Section: "Fragment Name Uniqueness"
|
||||
import { UniqueFragmentNames } from './rules/UniqueFragmentNames';
|
||||
|
||||
// Spec Section: "Fragment spread target defined"
|
||||
import { KnownFragmentNames } from './rules/KnownFragmentNames';
|
||||
|
||||
// Spec Section: "Fragments must be used"
|
||||
import { NoUnusedFragments } from './rules/NoUnusedFragments';
|
||||
|
||||
// Spec Section: "Fragment spread is possible"
|
||||
import { PossibleFragmentSpreads } from './rules/PossibleFragmentSpreads';
|
||||
|
||||
// Spec Section: "Fragments must not form cycles"
|
||||
import { NoFragmentCycles } from './rules/NoFragmentCycles';
|
||||
|
||||
// Spec Section: "Variable Uniqueness"
|
||||
import { UniqueVariableNames } from './rules/UniqueVariableNames';
|
||||
|
||||
// Spec Section: "All Variable Used Defined"
|
||||
import { NoUndefinedVariables } from './rules/NoUndefinedVariables';
|
||||
|
||||
// Spec Section: "All Variables Used"
|
||||
import { NoUnusedVariables } from './rules/NoUnusedVariables';
|
||||
|
||||
// Spec Section: "Directives Are Defined"
|
||||
import { KnownDirectives } from './rules/KnownDirectives';
|
||||
|
||||
// Spec Section: "Directives Are Unique Per Location"
|
||||
import { UniqueDirectivesPerLocation } from './rules/UniqueDirectivesPerLocation';
|
||||
|
||||
// Spec Section: "Argument Names"
|
||||
import { KnownArgumentNames } from './rules/KnownArgumentNames';
|
||||
|
||||
// Spec Section: "Argument Uniqueness"
|
||||
import { UniqueArgumentNames } from './rules/UniqueArgumentNames';
|
||||
|
||||
// Spec Section: "Value Type Correctness"
|
||||
import { ValuesOfCorrectType } from './rules/ValuesOfCorrectType';
|
||||
|
||||
// Spec Section: "Argument Optionality"
|
||||
import { ProvidedRequiredArguments } from './rules/ProvidedRequiredArguments';
|
||||
|
||||
// Spec Section: "All Variable Usages Are Allowed"
|
||||
import { VariablesInAllowedPosition } from './rules/VariablesInAllowedPosition';
|
||||
|
||||
// Spec Section: "Field Selection Merging"
|
||||
import { OverlappingFieldsCanBeMerged } from './rules/OverlappingFieldsCanBeMerged';
|
||||
|
||||
// Spec Section: "Input Object Field Uniqueness"
|
||||
import { UniqueInputFieldNames } from './rules/UniqueInputFieldNames';
|
||||
|
||||
/**
|
||||
* This set includes all validation rules defined by the GraphQL spec.
|
||||
*
|
||||
* The order of the rules in this list has been adjusted to lead to the
|
||||
* most clear output when encountering multiple validation errors.
|
||||
*/
|
||||
export const specifiedRules: $ReadOnlyArray<ValidationRule> = [
|
||||
ExecutableDefinitions,
|
||||
UniqueOperationNames,
|
||||
LoneAnonymousOperation,
|
||||
SingleFieldSubscriptions,
|
||||
KnownTypeNames,
|
||||
FragmentsOnCompositeTypes,
|
||||
VariablesAreInputTypes,
|
||||
ScalarLeafs,
|
||||
FieldsOnCorrectType,
|
||||
UniqueFragmentNames,
|
||||
KnownFragmentNames,
|
||||
NoUnusedFragments,
|
||||
PossibleFragmentSpreads,
|
||||
NoFragmentCycles,
|
||||
UniqueVariableNames,
|
||||
NoUndefinedVariables,
|
||||
NoUnusedVariables,
|
||||
KnownDirectives,
|
||||
UniqueDirectivesPerLocation,
|
||||
KnownArgumentNames,
|
||||
UniqueArgumentNames,
|
||||
ValuesOfCorrectType,
|
||||
ProvidedRequiredArguments,
|
||||
VariablesInAllowedPosition,
|
||||
OverlappingFieldsCanBeMerged,
|
||||
UniqueInputFieldNames,
|
||||
];
|
||||
|
||||
import { LoneSchemaDefinition } from './rules/LoneSchemaDefinition';
|
||||
import { KnownArgumentNamesOnDirectives } from './rules/KnownArgumentNames';
|
||||
import { ProvidedRequiredArgumentsOnDirectives } from './rules/ProvidedRequiredArguments';
|
||||
|
||||
// @internal
|
||||
export const specifiedSDLRules: $ReadOnlyArray<SDLValidationRule> = [
|
||||
LoneSchemaDefinition,
|
||||
KnownDirectives,
|
||||
UniqueDirectivesPerLocation,
|
||||
KnownArgumentNamesOnDirectives,
|
||||
UniqueArgumentNames,
|
||||
UniqueInputFieldNames,
|
||||
ProvidedRequiredArgumentsOnDirectives,
|
||||
];
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
// Spec Section: "Executable Definitions"
|
||||
import { ExecutableDefinitions } from './rules/ExecutableDefinitions'; // Spec Section: "Operation Name Uniqueness"
|
||||
|
||||
import { UniqueOperationNames } from './rules/UniqueOperationNames'; // Spec Section: "Lone Anonymous Operation"
|
||||
|
||||
import { LoneAnonymousOperation } from './rules/LoneAnonymousOperation'; // Spec Section: "Subscriptions with Single Root Field"
|
||||
|
||||
import { SingleFieldSubscriptions } from './rules/SingleFieldSubscriptions'; // Spec Section: "Fragment Spread Type Existence"
|
||||
|
||||
import { KnownTypeNames } from './rules/KnownTypeNames'; // Spec Section: "Fragments on Composite Types"
|
||||
|
||||
import { FragmentsOnCompositeTypes } from './rules/FragmentsOnCompositeTypes'; // Spec Section: "Variables are Input Types"
|
||||
|
||||
import { VariablesAreInputTypes } from './rules/VariablesAreInputTypes'; // Spec Section: "Leaf Field Selections"
|
||||
|
||||
import { ScalarLeafs } from './rules/ScalarLeafs'; // Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
|
||||
|
||||
import { FieldsOnCorrectType } from './rules/FieldsOnCorrectType'; // Spec Section: "Fragment Name Uniqueness"
|
||||
|
||||
import { UniqueFragmentNames } from './rules/UniqueFragmentNames'; // Spec Section: "Fragment spread target defined"
|
||||
|
||||
import { KnownFragmentNames } from './rules/KnownFragmentNames'; // Spec Section: "Fragments must be used"
|
||||
|
||||
import { NoUnusedFragments } from './rules/NoUnusedFragments'; // Spec Section: "Fragment spread is possible"
|
||||
|
||||
import { PossibleFragmentSpreads } from './rules/PossibleFragmentSpreads'; // Spec Section: "Fragments must not form cycles"
|
||||
|
||||
import { NoFragmentCycles } from './rules/NoFragmentCycles'; // Spec Section: "Variable Uniqueness"
|
||||
|
||||
import { UniqueVariableNames } from './rules/UniqueVariableNames'; // Spec Section: "All Variable Used Defined"
|
||||
|
||||
import { NoUndefinedVariables } from './rules/NoUndefinedVariables'; // Spec Section: "All Variables Used"
|
||||
|
||||
import { NoUnusedVariables } from './rules/NoUnusedVariables'; // Spec Section: "Directives Are Defined"
|
||||
|
||||
import { KnownDirectives } from './rules/KnownDirectives'; // Spec Section: "Directives Are Unique Per Location"
|
||||
|
||||
import { UniqueDirectivesPerLocation } from './rules/UniqueDirectivesPerLocation'; // Spec Section: "Argument Names"
|
||||
|
||||
import { KnownArgumentNames } from './rules/KnownArgumentNames'; // Spec Section: "Argument Uniqueness"
|
||||
|
||||
import { UniqueArgumentNames } from './rules/UniqueArgumentNames'; // Spec Section: "Value Type Correctness"
|
||||
|
||||
import { ValuesOfCorrectType } from './rules/ValuesOfCorrectType'; // Spec Section: "Argument Optionality"
|
||||
|
||||
import { ProvidedRequiredArguments } from './rules/ProvidedRequiredArguments'; // Spec Section: "All Variable Usages Are Allowed"
|
||||
|
||||
import { VariablesInAllowedPosition } from './rules/VariablesInAllowedPosition'; // Spec Section: "Field Selection Merging"
|
||||
|
||||
import { OverlappingFieldsCanBeMerged } from './rules/OverlappingFieldsCanBeMerged'; // Spec Section: "Input Object Field Uniqueness"
|
||||
|
||||
import { UniqueInputFieldNames } from './rules/UniqueInputFieldNames';
|
||||
/**
|
||||
* This set includes all validation rules defined by the GraphQL spec.
|
||||
*
|
||||
* The order of the rules in this list has been adjusted to lead to the
|
||||
* most clear output when encountering multiple validation errors.
|
||||
*/
|
||||
|
||||
export var specifiedRules = [ExecutableDefinitions, UniqueOperationNames, LoneAnonymousOperation, SingleFieldSubscriptions, KnownTypeNames, FragmentsOnCompositeTypes, VariablesAreInputTypes, ScalarLeafs, FieldsOnCorrectType, UniqueFragmentNames, KnownFragmentNames, NoUnusedFragments, PossibleFragmentSpreads, NoFragmentCycles, UniqueVariableNames, NoUndefinedVariables, NoUnusedVariables, KnownDirectives, UniqueDirectivesPerLocation, KnownArgumentNames, UniqueArgumentNames, ValuesOfCorrectType, ProvidedRequiredArguments, VariablesInAllowedPosition, OverlappingFieldsCanBeMerged, UniqueInputFieldNames];
|
||||
import { LoneSchemaDefinition } from './rules/LoneSchemaDefinition';
|
||||
import { KnownArgumentNamesOnDirectives } from './rules/KnownArgumentNames';
|
||||
import { ProvidedRequiredArgumentsOnDirectives } from './rules/ProvidedRequiredArguments'; // @internal
|
||||
|
||||
export var specifiedSDLRules = [LoneSchemaDefinition, KnownDirectives, UniqueDirectivesPerLocation, KnownArgumentNamesOnDirectives, UniqueArgumentNames, UniqueInputFieldNames, ProvidedRequiredArgumentsOnDirectives];
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.validate = validate;
|
||||
exports.validateSDL = validateSDL;
|
||||
exports.assertValidSDL = assertValidSDL;
|
||||
exports.assertValidSDLExtension = assertValidSDLExtension;
|
||||
|
||||
var _invariant = _interopRequireDefault(require("../jsutils/invariant"));
|
||||
|
||||
var _visitor = require("../language/visitor");
|
||||
|
||||
var _validate = require("../type/validate");
|
||||
|
||||
var _TypeInfo = require("../utilities/TypeInfo");
|
||||
|
||||
var _specifiedRules = require("./specifiedRules");
|
||||
|
||||
var _ValidationContext = require("./ValidationContext");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements the "Validation" section of the spec.
|
||||
*
|
||||
* Validation runs synchronously, returning an array of encountered errors, or
|
||||
* an empty array if no errors were encountered and the document is valid.
|
||||
*
|
||||
* A list of specific validation rules may be provided. If not provided, the
|
||||
* default list of rules defined by the GraphQL specification will be used.
|
||||
*
|
||||
* Each validation rules is a function which returns a visitor
|
||||
* (see the language/visitor API). Visitor methods are expected to return
|
||||
* GraphQLErrors, or Arrays of GraphQLErrors when invalid.
|
||||
*
|
||||
* Optionally a custom TypeInfo instance may be provided. If not provided, one
|
||||
* will be created from the provided schema.
|
||||
*/
|
||||
function validate(schema, documentAST) {
|
||||
var rules = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _specifiedRules.specifiedRules;
|
||||
var typeInfo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new _TypeInfo.TypeInfo(schema);
|
||||
!documentAST ? (0, _invariant.default)(0, 'Must provide document') : void 0; // If the schema used for validation is invalid, throw an error.
|
||||
|
||||
(0, _validate.assertValidSchema)(schema);
|
||||
var context = new _ValidationContext.ValidationContext(schema, documentAST, typeInfo); // This uses a specialized visitor which runs multiple visitors in parallel,
|
||||
// while maintaining the visitor skip and break API.
|
||||
|
||||
var visitor = (0, _visitor.visitInParallel)(rules.map(function (rule) {
|
||||
return rule(context);
|
||||
})); // Visit the whole document with each instance of all provided rules.
|
||||
|
||||
(0, _visitor.visit)(documentAST, (0, _visitor.visitWithTypeInfo)(typeInfo, visitor));
|
||||
return context.getErrors();
|
||||
} // @internal
|
||||
|
||||
|
||||
function validateSDL(documentAST, schemaToExtend) {
|
||||
var rules = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _specifiedRules.specifiedSDLRules;
|
||||
var context = new _ValidationContext.SDLValidationContext(documentAST, schemaToExtend);
|
||||
var visitors = rules.map(function (rule) {
|
||||
return rule(context);
|
||||
});
|
||||
(0, _visitor.visit)(documentAST, (0, _visitor.visitInParallel)(visitors));
|
||||
return context.getErrors();
|
||||
}
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
|
||||
function assertValidSDL(documentAST) {
|
||||
var errors = validateSDL(documentAST);
|
||||
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(function (error) {
|
||||
return error.message;
|
||||
}).join('\n\n'));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
|
||||
function assertValidSDLExtension(documentAST, schema) {
|
||||
var errors = validateSDL(documentAST, schema);
|
||||
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(function (error) {
|
||||
return error.message;
|
||||
}).join('\n\n'));
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
import invariant from '../jsutils/invariant';
|
||||
import type { GraphQLError } from '../error';
|
||||
import { visit, visitInParallel, visitWithTypeInfo } from '../language/visitor';
|
||||
import type { DocumentNode } from '../language/ast';
|
||||
import type { GraphQLSchema } from '../type/schema';
|
||||
import { assertValidSchema } from '../type/validate';
|
||||
import { TypeInfo } from '../utilities/TypeInfo';
|
||||
import { specifiedRules, specifiedSDLRules } from './specifiedRules';
|
||||
import type { SDLValidationRule, ValidationRule } from './ValidationContext';
|
||||
import { SDLValidationContext, ValidationContext } from './ValidationContext';
|
||||
|
||||
/**
|
||||
* Implements the "Validation" section of the spec.
|
||||
*
|
||||
* Validation runs synchronously, returning an array of encountered errors, or
|
||||
* an empty array if no errors were encountered and the document is valid.
|
||||
*
|
||||
* A list of specific validation rules may be provided. If not provided, the
|
||||
* default list of rules defined by the GraphQL specification will be used.
|
||||
*
|
||||
* Each validation rules is a function which returns a visitor
|
||||
* (see the language/visitor API). Visitor methods are expected to return
|
||||
* GraphQLErrors, or Arrays of GraphQLErrors when invalid.
|
||||
*
|
||||
* Optionally a custom TypeInfo instance may be provided. If not provided, one
|
||||
* will be created from the provided schema.
|
||||
*/
|
||||
export function validate(
|
||||
schema: GraphQLSchema,
|
||||
documentAST: DocumentNode,
|
||||
rules?: $ReadOnlyArray<ValidationRule> = specifiedRules,
|
||||
typeInfo?: TypeInfo = new TypeInfo(schema),
|
||||
): $ReadOnlyArray<GraphQLError> {
|
||||
invariant(documentAST, 'Must provide document');
|
||||
// If the schema used for validation is invalid, throw an error.
|
||||
assertValidSchema(schema);
|
||||
|
||||
const context = new ValidationContext(schema, documentAST, typeInfo);
|
||||
// This uses a specialized visitor which runs multiple visitors in parallel,
|
||||
// while maintaining the visitor skip and break API.
|
||||
const visitor = visitInParallel(rules.map(rule => rule(context)));
|
||||
// Visit the whole document with each instance of all provided rules.
|
||||
visit(documentAST, visitWithTypeInfo(typeInfo, visitor));
|
||||
return context.getErrors();
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function validateSDL(
|
||||
documentAST: DocumentNode,
|
||||
schemaToExtend?: ?GraphQLSchema,
|
||||
rules?: $ReadOnlyArray<SDLValidationRule> = specifiedSDLRules,
|
||||
): $ReadOnlyArray<GraphQLError> {
|
||||
const context = new SDLValidationContext(documentAST, schemaToExtend);
|
||||
const visitors = rules.map(rule => rule(context));
|
||||
visit(documentAST, visitInParallel(visitors));
|
||||
return context.getErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function assertValidSDL(documentAST: DocumentNode): void {
|
||||
const errors = validateSDL(documentAST);
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(error => error.message).join('\n\n'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function assertValidSDLExtension(
|
||||
documentAST: DocumentNode,
|
||||
schema: GraphQLSchema,
|
||||
): void {
|
||||
const errors = validateSDL(documentAST, schema);
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(error => error.message).join('\n\n'));
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* strict
|
||||
*/
|
||||
import invariant from '../jsutils/invariant';
|
||||
import { visit, visitInParallel, visitWithTypeInfo } from '../language/visitor';
|
||||
import { assertValidSchema } from '../type/validate';
|
||||
import { TypeInfo } from '../utilities/TypeInfo';
|
||||
import { specifiedRules, specifiedSDLRules } from './specifiedRules';
|
||||
import { SDLValidationContext, ValidationContext } from './ValidationContext';
|
||||
/**
|
||||
* Implements the "Validation" section of the spec.
|
||||
*
|
||||
* Validation runs synchronously, returning an array of encountered errors, or
|
||||
* an empty array if no errors were encountered and the document is valid.
|
||||
*
|
||||
* A list of specific validation rules may be provided. If not provided, the
|
||||
* default list of rules defined by the GraphQL specification will be used.
|
||||
*
|
||||
* Each validation rules is a function which returns a visitor
|
||||
* (see the language/visitor API). Visitor methods are expected to return
|
||||
* GraphQLErrors, or Arrays of GraphQLErrors when invalid.
|
||||
*
|
||||
* Optionally a custom TypeInfo instance may be provided. If not provided, one
|
||||
* will be created from the provided schema.
|
||||
*/
|
||||
|
||||
export function validate(schema, documentAST) {
|
||||
var rules = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : specifiedRules;
|
||||
var typeInfo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new TypeInfo(schema);
|
||||
!documentAST ? invariant(0, 'Must provide document') : void 0; // If the schema used for validation is invalid, throw an error.
|
||||
|
||||
assertValidSchema(schema);
|
||||
var context = new ValidationContext(schema, documentAST, typeInfo); // This uses a specialized visitor which runs multiple visitors in parallel,
|
||||
// while maintaining the visitor skip and break API.
|
||||
|
||||
var visitor = visitInParallel(rules.map(function (rule) {
|
||||
return rule(context);
|
||||
})); // Visit the whole document with each instance of all provided rules.
|
||||
|
||||
visit(documentAST, visitWithTypeInfo(typeInfo, visitor));
|
||||
return context.getErrors();
|
||||
} // @internal
|
||||
|
||||
export function validateSDL(documentAST, schemaToExtend) {
|
||||
var rules = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : specifiedSDLRules;
|
||||
var context = new SDLValidationContext(documentAST, schemaToExtend);
|
||||
var visitors = rules.map(function (rule) {
|
||||
return rule(context);
|
||||
});
|
||||
visit(documentAST, visitInParallel(visitors));
|
||||
return context.getErrors();
|
||||
}
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function assertValidSDL(documentAST) {
|
||||
var errors = validateSDL(documentAST);
|
||||
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(function (error) {
|
||||
return error.message;
|
||||
}).join('\n\n'));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Utility function which asserts a SDL document is valid by throwing an error
|
||||
* if it is invalid.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function assertValidSDLExtension(documentAST, schema) {
|
||||
var errors = validateSDL(documentAST, schema);
|
||||
|
||||
if (errors.length !== 0) {
|
||||
throw new Error(errors.map(function (error) {
|
||||
return error.message;
|
||||
}).join('\n\n'));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user