UNPKG

@graphql-inspector/cli

Version:

Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.

455 lines (454 loc) • 22.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.errors = void 0; exports.patchSchema = patchSchema; exports.groupByCoordinateAST = groupByCoordinateAST; exports.patchCoordinatesAST = patchCoordinatesAST; exports.patch = patch; const tslib_1 = require("tslib"); const graphql_1 = require("graphql"); const core_1 = require("@graphql-inspector/core"); const utils_1 = require("@graphql-tools/utils"); const errors_js_1 = require("./errors.js"); const directive_usages_js_1 = require("./patches/directive-usages.js"); const directives_js_1 = require("./patches/directives.js"); const enum_js_1 = require("./patches/enum.js"); const fields_js_1 = require("./patches/fields.js"); const inputs_js_1 = require("./patches/inputs.js"); const interfaces_js_1 = require("./patches/interfaces.js"); const schema_js_1 = require("./patches/schema.js"); const types_js_1 = require("./patches/types.js"); const unions_js_1 = require("./patches/unions.js"); const utils_js_1 = require("./utils.js"); exports.errors = tslib_1.__importStar(require("./errors.js")); /** * Wraps converting a schema to AST safely, patching, then rebuilding the schema from AST. * The schema is not validated in this function. That it is the responsibility of the caller. */ function patchSchema(schema, changes, config) { const ast = (0, graphql_1.parse)((0, utils_1.printSchemaWithDirectives)(schema, { assumeValid: true })); const patchedAst = patch(ast, changes, config); return (0, graphql_1.buildASTSchema)(patchedAst, { assumeValid: true, assumeValidSDL: true }); } /** * Extracts all the root definitions from a DocumentNode and creates a mapping of their coordinate * to the defined ASTNode. E.g. A field's coordinate is "Type.field". */ function groupByCoordinateAST(ast) { const schemaNodes = []; const nodesByCoordinate = new Map(); const pathArray = []; (0, graphql_1.visit)(ast, { enter(node, key) { switch (node.kind) { case graphql_1.Kind.ARGUMENT: case graphql_1.Kind.ENUM_TYPE_DEFINITION: case graphql_1.Kind.ENUM_TYPE_EXTENSION: case graphql_1.Kind.ENUM_VALUE_DEFINITION: case graphql_1.Kind.FIELD_DEFINITION: case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION: case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION: case graphql_1.Kind.INPUT_VALUE_DEFINITION: case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: case graphql_1.Kind.INTERFACE_TYPE_EXTENSION: case graphql_1.Kind.OBJECT_FIELD: case graphql_1.Kind.OBJECT_TYPE_DEFINITION: case graphql_1.Kind.OBJECT_TYPE_EXTENSION: case graphql_1.Kind.SCALAR_TYPE_DEFINITION: case graphql_1.Kind.SCALAR_TYPE_EXTENSION: case graphql_1.Kind.UNION_TYPE_DEFINITION: case graphql_1.Kind.UNION_TYPE_EXTENSION: { pathArray.push(node.name.value); const path = pathArray.join('.'); nodesByCoordinate.set(path, node); break; } case graphql_1.Kind.DIRECTIVE_DEFINITION: { pathArray.push(`@${node.name.value}`); const path = pathArray.join('.'); nodesByCoordinate.set(path, node); break; } case graphql_1.Kind.DIRECTIVE: { /** * Check if this directive is on the schema node. If so, then push an empty path * to distinguish it from the definitions */ const isRoot = pathArray.length === 0; if (isRoot) { pathArray.push(`.@${node.name.value}[${key}]`); } else { pathArray.push(`@${node.name.value}[${key}]`); } // const path = pathArray.join('.'); // nodesByCoordinate.set(path, node); // @note skip setting the node for directives because repeat directives screw this up. break; } case graphql_1.Kind.DOCUMENT: { break; } case graphql_1.Kind.SCHEMA_EXTENSION: case graphql_1.Kind.SCHEMA_DEFINITION: { // @todo There can be only one. Replace `schemaNodes` with using `nodesByCoordinate.get('')`. schemaNodes.push(node); nodesByCoordinate.set('', node); break; } // default: { // // by definition this things like return types, names, named nodes... // // it's nothing we want to collect. // return false; // } } }, leave(node) { switch (node.kind) { case graphql_1.Kind.ARGUMENT: case graphql_1.Kind.ENUM_TYPE_DEFINITION: case graphql_1.Kind.ENUM_TYPE_EXTENSION: case graphql_1.Kind.ENUM_VALUE_DEFINITION: case graphql_1.Kind.FIELD_DEFINITION: case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION: case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION: case graphql_1.Kind.INPUT_VALUE_DEFINITION: case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: case graphql_1.Kind.INTERFACE_TYPE_EXTENSION: case graphql_1.Kind.OBJECT_FIELD: case graphql_1.Kind.OBJECT_TYPE_DEFINITION: case graphql_1.Kind.OBJECT_TYPE_EXTENSION: case graphql_1.Kind.SCALAR_TYPE_DEFINITION: case graphql_1.Kind.SCALAR_TYPE_EXTENSION: case graphql_1.Kind.UNION_TYPE_DEFINITION: case graphql_1.Kind.UNION_TYPE_EXTENSION: case graphql_1.Kind.DIRECTIVE_DEFINITION: case graphql_1.Kind.DIRECTIVE: { pathArray.pop(); break; } } }, }); return [schemaNodes, nodesByCoordinate]; } function patchCoordinatesAST(schemaNodes, nodesByCoordinate, changes, patchConfig = {}) { const config = { onError: errors_js_1.defaultErrorHandler, debug: false, ...patchConfig, }; const context = { removedDirectiveNodes: [], }; for (const change of changes) { if (config.debug) { (0, utils_js_1.debugPrintChange)(change, nodesByCoordinate); } switch (change.type) { case core_1.ChangeType.SchemaMutationTypeChanged: { (0, schema_js_1.schemaMutationTypeChanged)(change, schemaNodes, config, context); break; } case core_1.ChangeType.SchemaQueryTypeChanged: { (0, schema_js_1.schemaQueryTypeChanged)(change, schemaNodes, config, context); break; } case core_1.ChangeType.SchemaSubscriptionTypeChanged: { (0, schema_js_1.schemaSubscriptionTypeChanged)(change, schemaNodes, config, context); break; } case core_1.ChangeType.DirectiveAdded: { (0, directives_js_1.directiveAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveRemoved: { (0, directives_js_1.directiveRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveArgumentAdded: { (0, directives_js_1.directiveArgumentAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveArgumentRemoved: { (0, directives_js_1.directiveArgumentRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveLocationAdded: { (0, directives_js_1.directiveLocationAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveLocationRemoved: { (0, directives_js_1.directiveLocationRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.EnumValueAdded: { (0, enum_js_1.enumValueAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldAdded: { (0, fields_js_1.fieldAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldRemoved: { (0, fields_js_1.fieldRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldTypeChanged: { (0, fields_js_1.fieldTypeChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldArgumentAdded: { (0, fields_js_1.fieldArgumentAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldArgumentTypeChanged: { (0, fields_js_1.fieldArgumentTypeChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldArgumentRemoved: { (0, fields_js_1.fieldArgumentRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldArgumentDescriptionChanged: { (0, fields_js_1.fieldArgumentDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldArgumentDefaultChanged: { (0, fields_js_1.fieldArgumentDefaultChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldDescriptionAdded: { (0, fields_js_1.fieldDescriptionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldDescriptionChanged: { (0, fields_js_1.fieldDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldAdded: { (0, inputs_js_1.inputFieldAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldRemoved: { (0, inputs_js_1.inputFieldRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldDescriptionAdded: { (0, inputs_js_1.inputFieldDescriptionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldTypeChanged: { (0, inputs_js_1.inputFieldTypeChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldDescriptionChanged: { (0, inputs_js_1.inputFieldDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldDescriptionRemoved: { (0, inputs_js_1.inputFieldDescriptionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.InputFieldDefaultValueChanged: { (0, inputs_js_1.inputFieldDefaultValueChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.ObjectTypeInterfaceAdded: { (0, interfaces_js_1.objectTypeInterfaceAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.ObjectTypeInterfaceRemoved: { (0, interfaces_js_1.objectTypeInterfaceRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.TypeDescriptionAdded: { (0, types_js_1.typeDescriptionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.TypeDescriptionChanged: { (0, types_js_1.typeDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.TypeDescriptionRemoved: { (0, types_js_1.typeDescriptionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.TypeAdded: { (0, types_js_1.typeAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.UnionMemberAdded: { (0, unions_js_1.unionMemberAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.UnionMemberRemoved: { (0, unions_js_1.unionMemberRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.TypeRemoved: { (0, types_js_1.typeRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.EnumValueRemoved: { (0, enum_js_1.enumValueRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.EnumValueDescriptionChanged: { (0, enum_js_1.enumValueDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.FieldDescriptionRemoved: { (0, fields_js_1.fieldDescriptionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveArgumentDefaultValueChanged: { (0, directives_js_1.directiveArgumentDefaultValueChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveArgumentDescriptionChanged: { (0, directives_js_1.directiveArgumentDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveArgumentTypeChanged: { (0, directives_js_1.directiveArgumentTypeChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveDescriptionChanged: { (0, directives_js_1.directiveDescriptionChanged)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveRepeatableAdded: { (0, directives_js_1.directiveRepeatableAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveRepeatableRemoved: { (0, directives_js_1.directiveRepeatableRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageArgumentDefinitionAdded: { (0, directive_usages_js_1.directiveUsageArgumentDefinitionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageArgumentDefinitionRemoved: { (0, directive_usages_js_1.directiveUsageArgumentDefinitionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageEnumAdded: { (0, directive_usages_js_1.directiveUsageEnumAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageEnumRemoved: { (0, directive_usages_js_1.directiveUsageEnumRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageEnumValueAdded: { (0, directive_usages_js_1.directiveUsageEnumValueAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageEnumValueRemoved: { (0, directive_usages_js_1.directiveUsageEnumValueRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageFieldAdded: { (0, directive_usages_js_1.directiveUsageFieldAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageFieldDefinitionAdded: { (0, directive_usages_js_1.directiveUsageFieldDefinitionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageFieldDefinitionRemoved: { (0, directive_usages_js_1.directiveUsageFieldDefinitionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageFieldRemoved: { (0, directive_usages_js_1.directiveUsageFieldRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInputFieldDefinitionAdded: { (0, directive_usages_js_1.directiveUsageInputFieldDefinitionAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInputFieldDefinitionRemoved: { (0, directive_usages_js_1.directiveUsageInputFieldDefinitionRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInputObjectAdded: { (0, directive_usages_js_1.directiveUsageInputObjectAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInputObjectRemoved: { (0, directive_usages_js_1.directiveUsageInputObjectRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInterfaceAdded: { (0, directive_usages_js_1.directiveUsageInterfaceAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageInterfaceRemoved: { (0, directive_usages_js_1.directiveUsageInterfaceRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageObjectAdded: { (0, directive_usages_js_1.directiveUsageObjectAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageObjectRemoved: { (0, directive_usages_js_1.directiveUsageObjectRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageScalarAdded: { (0, directive_usages_js_1.directiveUsageScalarAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageScalarRemoved: { (0, directive_usages_js_1.directiveUsageScalarRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageSchemaAdded: { (0, directive_usages_js_1.directiveUsageSchemaAdded)(change, schemaNodes, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageSchemaRemoved: { (0, directive_usages_js_1.directiveUsageSchemaRemoved)(change, schemaNodes, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageUnionMemberAdded: { (0, directive_usages_js_1.directiveUsageUnionMemberAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageUnionMemberRemoved: { (0, directive_usages_js_1.directiveUsageUnionMemberRemoved)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageArgumentAdded: { (0, directive_usages_js_1.directiveUsageArgumentAdded)(change, nodesByCoordinate, config, context); break; } case core_1.ChangeType.DirectiveUsageArgumentRemoved: { (0, directive_usages_js_1.directiveUsageArgumentRemoved)(change, nodesByCoordinate, config, context); break; } default: { console.log(`${change.type} is not implemented yet.`); } } } for (const node of context.removedDirectiveNodes) { node.directives = node.directives?.filter(d => d != null); } return { kind: graphql_1.Kind.DOCUMENT, // filter out the non-definition nodes (e.g. field definitions) definitions: [ ...schemaNodes, ...Array.from(nodesByCoordinate.values()).filter(graphql_1.isDefinitionNode), ], }; } /** This method wraps groupByCoordinateAST and patchCoordinatesAST for convenience. */ function patch(ast, changes, patchConfig) { const [schemaNodes, nodesByCoordinate] = groupByCoordinateAST(ast); return patchCoordinatesAST(schemaNodes, nodesByCoordinate, changes, patchConfig); }