UNPKG

@graphql-hive/core

Version:
198 lines (197 loc) • 8.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractCoordinates = extractCoordinates; const graphql_1 = require("graphql"); const add_hive_typenames_js_1 = require("../add-hive-typenames.js"); const docCache = new WeakMap(); const schemaOperationPlanCache = new WeakMap(); function extractCoordinates({ schema, document, resultData, }) { const stats = {}; let docInfo = docCache.get(document); if (!docInfo) { let operation = null; const fragments = {}; for (const def of document.definitions) { if (def.kind === graphql_1.Kind.FRAGMENT_DEFINITION) { fragments[def.name.value] = def; } else if (def.kind === graphql_1.Kind.OPERATION_DEFINITION) { if (!operation) operation = def; } } docInfo = { operation, fragments }; docCache.set(document, docInfo); } const { operation, fragments } = docInfo; if (!operation || !resultData) return stats; const rootType = operation.operation === 'mutation' ? schema.getMutationType() : operation.operation === 'subscription' ? schema.getSubscriptionType() : schema.getQueryType(); if (!rootType) return stats; let operationCache = schemaOperationPlanCache.get(schema); if (!operationCache) { operationCache = new WeakMap(); schemaOperationPlanCache.set(schema, operationCache); } let rootPlan = operationCache.get(operation); if (!rootPlan) { rootPlan = buildPlanNode(schema, [operation.selectionSet], rootType, fragments); operationCache.set(operation, rootPlan); } traverseData(resultData, rootPlan, rootType.name, stats); return stats; } function buildPlanNode(schema, selectionSets, parentType, fragments) { const planNode = new Map(); const namedType = (0, graphql_1.getNamedType)(parentType); let possibleTypes; if ((0, graphql_1.isAbstractType)(namedType)) { possibleTypes = schema.getPossibleTypes(namedType); } else { possibleTypes = [namedType]; } for (const concreteType of possibleTypes) { const typePlan = { typeCoords: [concreteType.name], fields: {}, isEnum: (0, graphql_1.isEnumType)(concreteType), enumName: (0, graphql_1.isEnumType)(concreteType) ? concreteType.name : null, }; if ((0, graphql_1.isObjectType)(concreteType)) { for (const iface of concreteType.getInterfaces()) { if (iface.name !== namedType.name && iface.name !== concreteType.name) { typePlan.typeCoords.push(iface.name); } } if (namedType.name !== concreteType.name && !typePlan.typeCoords.includes(namedType.name)) { typePlan.typeCoords.push(namedType.name); } } else if (namedType.name !== concreteType.name) { typePlan.typeCoords.push(namedType.name); } typePlan.typeCoords = Array.from(new Set(typePlan.typeCoords)); if (selectionSets.length > 0 && (0, graphql_1.isObjectType)(concreteType)) { const mergedFields = collectFieldsForType(selectionSets, concreteType, schema, fragments); for (const responseKey in mergedFields) { const { fieldName, selectionSets: fieldSelSets } = mergedFields[responseKey]; const fieldDef = concreteType.getFields()[fieldName]; if (!fieldDef) continue; const fieldCoords = [`${concreteType.name}.${fieldName}`]; for (const iface of concreteType.getInterfaces()) { if (iface.getFields()[fieldName]) { fieldCoords.push(`${iface.name}.${fieldName}`); } } const fieldNamedType = (0, graphql_1.getNamedType)(fieldDef.type); const childPlan = buildPlanNode(schema, fieldSelSets, fieldNamedType, fragments); typePlan.fields[responseKey] = { fieldCoords, childPlan, expectedTypeName: fieldNamedType.name, }; } } planNode.set(concreteType.name, typePlan); } return planNode; } function collectFieldsForType(selectionSets, concreteType, schema, fragments) { const fields = {}; function visit(selectionSet) { var _a; for (const selection of selectionSet.selections) { if (selection.kind === graphql_1.Kind.FIELD) { const responseKey = selection.alias ? selection.alias.value : selection.name.value; const fieldName = selection.name.value; if (fieldName === add_hive_typenames_js_1.HIVE_INTERNAL_TYPENAME || fieldName === '__typename') continue; if (!fields[responseKey]) { fields[responseKey] = { fieldName, selectionSets: [] }; } if (selection.selectionSet) { fields[responseKey].selectionSets.push(selection.selectionSet); } } else if (selection.kind === graphql_1.Kind.FRAGMENT_SPREAD) { const frag = fragments[selection.name.value]; if (frag && doesTypeMatch(frag.typeCondition.name.value, concreteType, schema)) { visit(frag.selectionSet); } } else if (selection.kind === graphql_1.Kind.INLINE_FRAGMENT) { const typeCondition = (_a = selection.typeCondition) === null || _a === void 0 ? void 0 : _a.name.value; if (!typeCondition || doesTypeMatch(typeCondition, concreteType, schema)) { visit(selection.selectionSet); } } } } for (const selSet of selectionSets) { visit(selSet); } return fields; } function doesTypeMatch(typeCondition, concreteType, schema) { if (typeCondition === concreteType.name) return true; const conditionType = schema.getType(typeCondition); if (!conditionType) return false; if ((0, graphql_1.isInterfaceType)(conditionType) && (0, graphql_1.isObjectType)(concreteType)) { return concreteType.getInterfaces().some(i => i.name === typeCondition); } if ((0, graphql_1.isAbstractType)(conditionType)) { return schema.getPossibleTypes(conditionType).some(t => t.name === concreteType.name); } return false; } function traverseData(data, planNode, fallbackTypeName, stats) { var _a, _b; if (data === null || data === undefined || !planNode) return; if (Array.isArray(data)) { for (let i = 0; i < data.length; i++) { traverseData(data[i], planNode, fallbackTypeName, stats); } return; } const typeName = (_b = (_a = data[add_hive_typenames_js_1.HIVE_INTERNAL_TYPENAME]) !== null && _a !== void 0 ? _a : data.__typename) !== null && _b !== void 0 ? _b : fallbackTypeName; const typePlan = planNode.get(typeName) || planNode.get(fallbackTypeName); if (!typePlan) { stats[fallbackTypeName] = (stats[fallbackTypeName] || 0) + 1; return; } for (let i = 0; i < typePlan.typeCoords.length; i++) { const coord = typePlan.typeCoords[i]; stats[coord] = (stats[coord] || 0) + 1; } if (typePlan.isEnum && typeof data === 'string' && typePlan.enumName) { const enumCoord = `${typePlan.enumName}.${data}`; stats[enumCoord] = (stats[enumCoord] || 0) + 1; return; } if (typeof data !== 'object') return; for (const responseKey in data) { if (responseKey === add_hive_typenames_js_1.HIVE_INTERNAL_TYPENAME || responseKey === '__typename') continue; const fieldPlan = typePlan.fields[responseKey]; if (!fieldPlan) continue; for (let i = 0; i < fieldPlan.fieldCoords.length; i++) { const coord = fieldPlan.fieldCoords[i]; stats[coord] = (stats[coord] || 0) + 1; } if (fieldPlan.childPlan) { traverseData(data[responseKey], fieldPlan.childPlan, fieldPlan.expectedTypeName, stats); } } }