@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
289 lines • 14.5 kB
JavaScript
;
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@graphql-tools/utils");
const graphql_1 = require("graphql");
const specifiedRules_1 = require("graphql/validation/specifiedRules");
const pluralize_1 = __importDefault(require("pluralize"));
const directives = __importStar(require("../../graphql/directives"));
const authentication_1 = require("../../graphql/directives/type-dependant-directives/authentication");
const authorization_1 = require("../../graphql/directives/type-dependant-directives/authorization");
const scaffolds_1 = require("../../graphql/directives/type-dependant-directives/scaffolds");
const subscriptions_authorization_1 = require("../../graphql/directives/type-dependant-directives/subscriptions-authorization");
const SortDirection_1 = require("../../graphql/enums/SortDirection");
const CartesianPointDistance_1 = require("../../graphql/input-objects/CartesianPointDistance");
const CartesianPointInput_1 = require("../../graphql/input-objects/CartesianPointInput");
const PointDistance_1 = require("../../graphql/input-objects/PointDistance");
const PointInput_1 = require("../../graphql/input-objects/PointInput");
const CartesianPoint_1 = require("../../graphql/objects/CartesianPoint");
const Point_1 = require("../../graphql/objects/Point");
const scalars = __importStar(require("../../graphql/scalars"));
const is_root_type_1 = require("../../utils/is-root-type");
const authentication_2 = require("./custom-rules/directives/authentication");
const authorization_2 = require("./custom-rules/directives/authorization");
const coalesce_1 = require("./custom-rules/directives/coalesce");
const cypher_1 = require("./custom-rules/directives/cypher");
const default_1 = require("./custom-rules/directives/default");
const fulltext_1 = require("./custom-rules/directives/fulltext");
const id_1 = require("./custom-rules/directives/id");
const limit_1 = require("./custom-rules/directives/limit");
const populated_by_1 = require("./custom-rules/directives/populated-by");
const relationship_1 = require("./custom-rules/directives/relationship");
const relay_id_1 = require("./custom-rules/directives/relay-id");
const subscriptionAuthorization_1 = require("./custom-rules/directives/subscriptionAuthorization");
const timestamp_1 = require("./custom-rules/directives/timestamp");
const error_single_relationships_1 = require("./custom-rules/error-single-relationships");
const valid_jwt_directives_1 = require("./custom-rules/features/valid-jwt-directives");
const valid_relationship_declaration_1 = require("./custom-rules/features/valid-relationship-declaration");
const valid_relationship_properties_1 = require("./custom-rules/features/valid-relationship-properties");
const valid_relay_id_1 = require("./custom-rules/features/valid-relay-id");
const directive_multiple_inheritance_1 = require("./custom-rules/valid-types/directive-multiple-inheritance");
const reserved_type_names_1 = require("./custom-rules/valid-types/reserved-type-names");
const valid_directive_combination_1 = require("./custom-rules/valid-types/valid-directive-combination");
const valid_field_types_1 = require("./custom-rules/valid-types/valid-field-types");
const valid_list_in_node_type_1 = require("./custom-rules/valid-types/valid-list-in-node-type");
const valid_object_type_1 = require("./custom-rules/valid-types/valid-object-type");
const valid_union_type_1 = require("./custom-rules/valid-types/valid-union-type");
const validate_neo4j_directive_arguments_value_1 = require("./custom-rules/validate-neo4j-directive-arguments-value");
const authorization_feature_disabled_1 = require("./custom-rules/warnings/authorization-feature-disabled");
const limit_max_can_be_bypassed_1 = require("./custom-rules/warnings/limit-max-can-be-bypassed");
const object_fields_without_resolver_1 = require("./custom-rules/warnings/object-fields-without-resolver");
const subscriptions_authorization_missing_1 = require("./custom-rules/warnings/subscriptions-authorization-missing");
const validate_schema_customizations_1 = require("./validate-schema-customizations");
const validate_sdl_1 = require("./validate-sdl");
function filterDocument(document, filterDirectives = false) {
const nodeNames = document.definitions
.filter((definition) => {
if (definition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
if (!(0, is_root_type_1.isRootType)(definition)) {
return true;
}
}
return false;
})
.map((definition) => definition.name.value);
const getArgumentType = (type) => {
if (type.kind === graphql_1.Kind.LIST_TYPE) {
return getArgumentType(type.type);
}
if (type.kind === graphql_1.Kind.NON_NULL_TYPE) {
return getArgumentType(type.type);
}
return type.name.value;
};
const filterInputTypes = (fields) => {
return fields?.filter((f) => {
const type = getArgumentType(f.type);
const nodeMatch = /(?<nodeName>.+)(?:ConnectInput|ConnectWhere|CreateInput|DeleteInput|DisconnectInput|Options|RelationInput|Sort|UpdateInput|Where)/gm.exec(type);
if (nodeMatch?.groups?.nodeName) {
if (nodeNames.includes(nodeMatch.groups.nodeName)) {
return false;
}
}
return true;
});
};
const filterFields = (fields) => {
return fields
?.filter((field) => {
const type = getArgumentType(field.type);
const match = /(?:Create|Update)(?<nodeName>.+)MutationResponse/gm.exec(type);
if (match?.groups?.nodeName) {
if (nodeNames.map((nodeName) => (0, pluralize_1.default)(nodeName)).includes(match.groups.nodeName)) {
return false;
}
}
return true;
})
.map((field) => {
return {
...field,
arguments: filterInputTypes(field.arguments),
directives: filterDirectives ? filterDirectiveNodes(field.directives) : field.directives,
};
});
};
// currentDirectiveDirective is of type ConstDirectiveNode, has to be any to support GraphQL 15
const filterDirectiveNodes = (directives) => {
return directives?.filter((directive) => {
return ![
authentication_1.authenticationDirectiveScaffold.name,
authorization_1.authorizationDirectiveScaffold.name,
subscriptions_authorization_1.subscriptionsAuthorizationDirectiveScaffold.name,
].includes(directive.name.value);
});
};
const filteredDocument = {
...document,
definitions: document.definitions.reduce((res, def) => {
if (def.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
const fields = filterInputTypes(def.fields);
if (!fields?.length) {
return res;
}
return [
...res,
{
...def,
fields,
},
];
}
if (def.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || def.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
if (!def.fields?.length) {
return [...res, def];
}
const fields = filterFields(def.fields);
if (!fields?.length) {
return res;
}
return [
...res,
{
...def,
fields,
directives: filterDirectives ? filterDirectiveNodes(def.directives) : def.directives,
},
];
}
return [...res, def];
}, []),
};
return filteredDocument;
}
function runValidationRulesOnFilteredDocument({ schema, document, userCustomResolvers, features, }) {
const errors = (0, validate_sdl_1.validateSDL)(document, [
...specifiedRules_1.specifiedSDLRules,
valid_directive_combination_1.DirectiveCombinationValid,
valid_directive_combination_1.SchemaOrTypeDirectives,
valid_jwt_directives_1.ValidJwtDirectives,
valid_relay_id_1.ValidRelayID,
valid_relationship_properties_1.ValidRelationshipProperties,
valid_relationship_declaration_1.ValidRelationshipDeclaration,
valid_field_types_1.ValidFieldTypes,
reserved_type_names_1.ReservedTypeNames,
valid_object_type_1.ValidObjectType,
valid_union_type_1.ValidUnionType,
directive_multiple_inheritance_1.ValidDirectiveInheritance,
validate_neo4j_directive_arguments_value_1.ValidateNeo4jDirectiveArgumentsValue,
(0, authorization_feature_disabled_1.WarnIfAuthorizationFeatureDisabled)(features?.authorization),
error_single_relationships_1.ErrorIfSingleRelationships,
(0, limit_max_can_be_bypassed_1.WarnIfAMaxLimitCanBeBypassedThroughInterface)(),
(0, object_fields_without_resolver_1.WarnObjectFieldsWithoutResolver)({
customResolvers: (0, utils_1.asArray)(userCustomResolvers ?? []),
}),
valid_list_in_node_type_1.ValidListInNodeType,
(0, subscriptions_authorization_missing_1.WarnIfSubscriptionsAuthorizationMissing)(Boolean(features?.subscriptions)),
// directives validations
authorization_2.validateAuthorizationDirective,
authentication_2.validateAuthenticationDirective,
coalesce_1.validateCoalesceDirective,
cypher_1.validateCypherDirective,
default_1.validateDefaultDirective,
fulltext_1.validateFulltextDirective,
id_1.validateIdDirective,
limit_1.validateLimitDirective,
populated_by_1.validatePopulatedByDirective,
relationship_1.validateRelationshipDirective,
relay_id_1.validateRelayIdDirective,
timestamp_1.validateTimestampDirective,
subscriptionAuthorization_1.validateSubscriptionAuthorizationDirective,
], schema, features?.populatedBy?.callbacks);
const filteredErrors = errors.filter((e) => e.message !== "Query root type must be provided.");
if (filteredErrors.length) {
throw filteredErrors;
}
}
function validateDocument({ document, features, additionalDefinitions, userCustomResolvers, }) {
const filteredDocument = filterDocument(document);
const { additionalDirectives, additionalTypes } = additionalDefinitions;
const schemaToExtend = new graphql_1.GraphQLSchema({
directives: [
...Object.values(directives),
...scaffolds_1.typeDependantDirectivesScaffolds,
...graphql_1.specifiedDirectives,
...(additionalDirectives || []),
],
types: [
...Object.values(scalars),
Point_1.Point,
CartesianPoint_1.CartesianPoint,
PointInput_1.PointInput,
PointDistance_1.PointDistance,
CartesianPointInput_1.CartesianPointInput,
CartesianPointDistance_1.CartesianPointDistance,
SortDirection_1.SortDirection,
...(additionalTypes || []),
],
});
runValidationRulesOnFilteredDocument({
schema: schemaToExtend,
document: filteredDocument,
userCustomResolvers,
features,
});
const schema = (0, graphql_1.extendSchema)(schemaToExtend, filteredDocument);
const errors = (0, graphql_1.validateSchema)(schema);
const filteredErrors = errors.filter((e) => e.message !== "Query root type must be provided.");
if (filteredErrors.length) {
throw filteredErrors;
}
// TODO: how to improve this??
// validates `@customResolver`, which needs to additionally have authorization directives filtered out for mergeSchemas not to error
(0, validate_schema_customizations_1.validateSchemaCustomizations)({ document, schema: (0, graphql_1.extendSchema)(schemaToExtend, filterDocument(document, true)) });
}
exports.default = validateDocument;
//# sourceMappingURL=validate-document.js.map