UNPKG

@neo4j/graphql

Version:

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations

289 lines 14.5 kB
"use strict"; /* * 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