UNPKG

@graphql-hive/gateway-plugin-console-sdk

Version:

GraphQL Hive + GraphQL Hive Gateway

132 lines (131 loc) • 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addTypenames = addTypenames; const graphql_1 = require("graphql"); const TYPENAME_FIELD = { kind: 'Field', name: { kind: 'Name', value: '__typename' }, }; /** * Recursively adds __typename to every selection set whose parent type is * abstract (union or interface), or whose parent type is a concrete object * type that implements an abstract type (i.e. it appears as a possible type * of some interface or union in the schema). * * Requires the schema so it can resolve field return types as it walks the * document tree. */ function addTypenames(document, schema) { let definitionsChanged = false; let newDefinitions = null; for (let i = 0; i < document.definitions.length; i++) { const def = document.definitions[i]; let newDef = def; if (def.kind === 'OperationDefinition') { const rootType = def.operation === 'query' ? schema.getQueryType() : def.operation === 'mutation' ? schema.getMutationType() : schema.getSubscriptionType(); if (rootType) { const newSelectionSet = walkSelectionSet(def.selectionSet, rootType, false, schema); if (newSelectionSet !== def.selectionSet) { newDef = Object.assign(Object.assign({}, def), { selectionSet: newSelectionSet }); } } } else if (def.kind === 'FragmentDefinition') { const onType = schema.getType(def.typeCondition.name.value); if ((0, graphql_1.isCompositeType)(onType)) { const newSelectionSet = walkSelectionSet(def.selectionSet, onType, (0, graphql_1.isAbstractType)(onType), schema); if (newSelectionSet !== def.selectionSet) { newDef = Object.assign(Object.assign({}, def), { selectionSet: newSelectionSet }); } } } if (newDef !== def) { if (!definitionsChanged) { definitionsChanged = true; newDefinitions = document.definitions.slice(0, i); } } if (definitionsChanged && newDefinitions) { newDefinitions.push(newDef); } } if (!definitionsChanged) { return document; } return Object.assign(Object.assign({}, document), { definitions: newDefinitions }); } function walkSelectionSet(selectionSet, parentType, parentIsAbstract, schema) { const thisTypeIsAbstract = (0, graphql_1.isAbstractType)(parentType); const checkTypename = thisTypeIsAbstract || (parentIsAbstract && (0, graphql_1.isObjectType)(parentType) && parentType.getInterfaces().length > 0); let selectionsChanged = false; let newSelections = null; let hasTypename = false; const selections = selectionSet.selections; for (let i = 0; i < selections.length; i++) { const selection = selections[i]; let newSelection = selection; if (selection.kind === 'Field') { if (checkTypename && selection.name.value === '__typename') { hasTypename = true; } if (selection.selectionSet) { const fieldDef = getFieldDef(parentType, selection); if (fieldDef) { const fieldType = (0, graphql_1.getNamedType)(fieldDef.type); if ((0, graphql_1.isCompositeType)(fieldType)) { const newSelectionSet = walkSelectionSet(selection.selectionSet, fieldType, (0, graphql_1.isAbstractType)(fieldType), schema); if (newSelectionSet !== selection.selectionSet) { newSelection = Object.assign(Object.assign({}, selection), { selectionSet: newSelectionSet }); } } } } } else if (selection.kind === 'InlineFragment') { const branchType = selection.typeCondition ? schema.getType(selection.typeCondition.name.value) : parentType; if ((0, graphql_1.isCompositeType)(branchType)) { const newSelectionSet = walkSelectionSet(selection.selectionSet, branchType, (0, graphql_1.isAbstractType)(branchType), schema); if (newSelectionSet !== selection.selectionSet) { newSelection = Object.assign(Object.assign({}, selection), { selectionSet: newSelectionSet }); } } } if (newSelection !== selection) { if (!selectionsChanged) { selectionsChanged = true; // This logic avoids copying the selection list before it's absolutely necessary. // Therefore, no additional memory will be wasted copying when iterating over // concrete, non-implementing object types. newSelections = selections.slice(0, i); } } if (selectionsChanged && newSelections) { newSelections.push(newSelection); } } const needsTypename = checkTypename && !hasTypename; if (!needsTypename && !selectionsChanged) { return selectionSet; } const finalSelections = selectionsChanged && newSelections ? newSelections : [...selections]; if (needsTypename) { finalSelections.push(TYPENAME_FIELD); } return Object.assign(Object.assign({}, selectionSet), { selections: finalSelections }); } function getFieldDef(parentType, field) { var _a; const name = field.name.value; // ignore internal fields like __schema, __type, and __typename if (name.startsWith('__')) { return null; } return 'getFields' in parentType ? ((_a = parentType.getFields()[name]) !== null && _a !== void 0 ? _a : null) : null; }