UNPKG

@apollo/query-graphs

Version:

Apollo Federation library to work with 'query graphs'

300 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.traversePathTree = exports.isRootPathTree = exports.PathTree = void 0; const federation_internals_1 = require("@apollo/federation-internals"); const querygraph_1 = require("./querygraph"); const pathContext_1 = require("./pathContext"); function opTriggerEquality(t1, t2) { if (t1 === t2) { return true; } if ((0, pathContext_1.isPathContext)(t1)) { return (0, pathContext_1.isPathContext)(t2) && t1.equals(t2); } if ((0, pathContext_1.isPathContext)(t2)) { return false; } return t1.equals(t2); } function findTriggerIdx(triggerEquality, forIndex, trigger) { for (let i = 0; i < forIndex.length; i++) { if (triggerEquality(forIndex[i][0], trigger)) { return i; } } return -1; } class PathTree { constructor(graph, vertex, localSelections, triggerEquality, childs) { this.graph = graph; this.vertex = vertex; this.localSelections = localSelections; this.triggerEquality = triggerEquality; this.childs = childs; } static create(graph, root, triggerEquality) { return new PathTree(graph, root, undefined, triggerEquality, []); } static createOp(graph, root) { return this.create(graph, root, opTriggerEquality); } static createFromOpPaths(graph, root, paths) { (0, federation_internals_1.assert)(paths.length > 0, `Should compute on empty paths`); return this.createFromPaths(graph, opTriggerEquality, root, paths.map(({ path, selection }) => ({ path: path[Symbol.iterator](), selection }))); } static createFromPaths(graph, triggerEquality, currentVertex, pathAndSelections) { const maxEdges = graph.outEdgesCount(currentVertex); const forEdgeIndex = new Array(maxEdges + 1); const newVertices = new Array(maxEdges); const order = new Array(maxEdges + 1); let currentOrder = 0; let totalChilds = 0; let localSelections = undefined; for (const ps of pathAndSelections) { const iterResult = ps.path.next(); if (iterResult.done) { if (ps.selection) { localSelections = localSelections ? localSelections.concat(ps.selection) : [ps.selection]; } continue; } const [edge, trigger, conditions, contextToSelection, parameterToContext] = iterResult.value; const idx = edge ? edge.index : maxEdges; if (edge) { newVertices[idx] = edge.tail; } const forIndex = forEdgeIndex[idx]; if (forIndex) { const triggerIdx = findTriggerIdx(triggerEquality, forIndex, trigger); if (triggerIdx < 0) { forIndex.push([trigger, conditions, [ps], contextToSelection, parameterToContext]); totalChilds++; } else { const existing = forIndex[triggerIdx]; const existingCond = existing[1]; const mergedConditions = existingCond ? (conditions ? existingCond.mergeIfNotEqual(conditions) : existingCond) : conditions; const newPaths = existing[2]; const mergedContextToSelection = (0, federation_internals_1.composeSets)(existing[3], contextToSelection); const mergedParameterToContext = (0, federation_internals_1.mergeMapOrNull)(existing[4], parameterToContext); newPaths.push(ps); forIndex[triggerIdx] = [trigger, mergedConditions, newPaths, mergedContextToSelection, mergedParameterToContext]; } } else { order[currentOrder++] = idx; forEdgeIndex[idx] = [[trigger, conditions, [ps], contextToSelection, parameterToContext]]; totalChilds++; } } const childs = new Array(totalChilds); let idx = 0; for (let i = 0; i < currentOrder; i++) { const edgeIndex = order[i]; const index = (edgeIndex === maxEdges ? null : edgeIndex); const newVertex = index === null ? currentVertex : newVertices[edgeIndex]; const values = forEdgeIndex[edgeIndex]; for (const [trigger, conditions, subPathAndSelections, contextToSelection, parameterToContext] of values) { childs[idx++] = { index, trigger, conditions, tree: this.createFromPaths(graph, triggerEquality, newVertex, subPathAndSelections), contextToSelection, parameterToContext, }; } } (0, federation_internals_1.assert)(idx === totalChilds, () => `Expected to have ${totalChilds} childs but only ${idx} added`); return new PathTree(graph, currentVertex, localSelections, triggerEquality, childs); } childCount() { return this.childs.length; } isLeaf() { return this.childCount() === 0; } *childElements(reverseOrder = false) { if (reverseOrder) { for (let i = this.childs.length - 1; i >= 0; i--) { yield this.element(i); } } else { for (let i = 0; i < this.childs.length; i++) { yield this.element(i); } } } element(i) { const child = this.childs[i]; return [ (child.index === null ? null : this.graph.outEdge(this.vertex, child.index)), child.trigger, child.conditions, child.tree, child.contextToSelection, child.parameterToContext, ]; } mergeChilds(c1, c2) { const cond1 = c1.conditions; const cond2 = c2.conditions; return { index: c1.index, trigger: c1.trigger, conditions: cond1 ? (cond2 ? cond1.mergeIfNotEqual(cond2) : cond1) : cond2, tree: c1.tree.merge(c2.tree), contextToSelection: (0, federation_internals_1.composeSets)(c1.contextToSelection, c2.contextToSelection), parameterToContext: (0, federation_internals_1.mergeMapOrNull)(c1.parameterToContext, c2.parameterToContext), }; } mergeIfNotEqual(other) { if (this.equalsSameRoot(other)) { return this; } return this.merge(other); } mergeLocalSelectionsWith(other) { return this.localSelections ? (other.localSelections ? this.localSelections.concat(other.localSelections) : this.localSelections) : other.localSelections; } merge(other) { if (this === other) { return this; } (0, federation_internals_1.assert)(other.graph === this.graph, 'Cannot merge path tree build on another graph'); (0, federation_internals_1.assert)(other.vertex.index === this.vertex.index, () => `Cannot merge path tree rooted at vertex ${other.vertex} into tree rooted at other vertex ${this.vertex}`); if (!other.childs.length) { return this; } if (!this.childs.length) { return other; } const localSelections = this.mergeLocalSelectionsWith(other); const mergeIndexes = new Array(other.childs.length); let countToAdd = 0; for (let i = 0; i < other.childs.length; i++) { const otherChild = other.childs[i]; const idx = this.findIndex(otherChild.trigger, otherChild.index); mergeIndexes[i] = idx; if (idx < 0) { ++countToAdd; } } const thisSize = this.childs.length; const newSize = thisSize + countToAdd; const newChilds = (0, federation_internals_1.copyWitNewLength)(this.childs, newSize); let addIdx = thisSize; for (let i = 0; i < other.childs.length; i++) { const idx = mergeIndexes[i]; if (idx < 0) { newChilds[addIdx++] = other.childs[i]; } else { newChilds[idx] = this.mergeChilds(newChilds[idx], other.childs[i]); } } (0, federation_internals_1.assert)(addIdx === newSize, () => `Expected ${newSize} childs but only got ${addIdx}`); return new PathTree(this.graph, this.vertex, localSelections, this.triggerEquality, newChilds); } equalsSameRoot(that) { if (this === that) { return true; } return (0, federation_internals_1.arrayEquals)(this.childs, that.childs, (c1, c2) => { return c1.index === c2.index && c1.trigger === c2.trigger && (c1.conditions ? (c2.conditions ? c1.conditions.equalsSameRoot(c2.conditions) : false) : !c2.conditions) && c1.tree.equalsSameRoot(c2.tree) && (0, federation_internals_1.setsEqual)(c1.contextToSelection, c2.contextToSelection) && PathTree.parameterToContextEquals(c1.parameterToContext, c2.parameterToContext); }); } static parameterToContextEquals(ptc1, ptc2) { var _a, _b; if (ptc1 === ptc2) { return true; } const thisKeys = Array.from((_a = ptc1 === null || ptc1 === void 0 ? void 0 : ptc1.keys()) !== null && _a !== void 0 ? _a : []); const thatKeys = Array.from((_b = ptc2 === null || ptc2 === void 0 ? void 0 : ptc2.keys()) !== null && _b !== void 0 ? _b : []); if (thisKeys.length !== thatKeys.length) { return false; } for (const key of thisKeys) { const thisSelection = ptc1.get(key); const thatSelection = ptc2.get(key); (0, federation_internals_1.assert)(thisSelection, () => `Expected to have a selection for key ${key}`); if (!thatSelection || (thisSelection.contextId !== thatSelection.contextId) || !(0, federation_internals_1.arrayEquals)(thisSelection.relativePath, thatSelection.relativePath) || !thisSelection.selectionSet.equals(thatSelection.selectionSet) || (thisSelection.subgraphArgType !== thatSelection.subgraphArgType)) { return false; } } return true; } concat(other) { (0, federation_internals_1.assert)(other.graph === this.graph, 'Cannot concat path tree build on another graph'); (0, federation_internals_1.assert)(other.vertex.index === this.vertex.index, () => `Cannot concat path tree rooted at vertex ${other.vertex} into tree rooted at other vertex ${this.vertex}`); if (!other.childs.length) { return this; } if (!this.childs.length) { return other; } const localSelections = this.mergeLocalSelectionsWith(other); const newChilds = this.childs.concat(other.childs); return new PathTree(this.graph, this.vertex, localSelections, this.triggerEquality, newChilds); } findIndex(trigger, edgeIndex) { for (let i = 0; i < this.childs.length; i++) { const child = this.childs[i]; if (child.index === edgeIndex && this.triggerEquality(child.trigger, trigger)) { return i; } } return -1; } isAllInSameSubgraph() { return this.isAllInSameSubgraphInternal(this.vertex.source); } isAllInSameSubgraphInternal(target) { return this.vertex.source === target && this.childs.every(c => c.tree.isAllInSameSubgraphInternal(target)); } toString(indent = "", includeConditions = false) { return this.toStringInternal(indent, includeConditions); } toStringInternal(indent, includeConditions) { if (this.isLeaf()) { return this.vertex.toString(); } return this.vertex + ':\n' + this.childs.map(child => indent + ` -> [${child.index}] ` + (includeConditions && child.conditions ? `!! {\n${indent + " "}${child.conditions.toString(indent + " ", true)}\n${indent} } ` : "") + `${child.trigger} = ` + child.tree.toStringInternal(indent + " ", includeConditions)).join('\n'); } } exports.PathTree = PathTree; function isRootPathTree(tree) { return (0, querygraph_1.isRootVertex)(tree.vertex); } exports.isRootPathTree = isRootPathTree; function traversePathTree(pathTree, onEdges) { for (const [edge, _, conditions, childTree] of pathTree.childElements()) { if (edge) { onEdges(edge); } if (conditions) { traversePathTree(conditions, onEdges); } traversePathTree(childTree, onEdges); } } exports.traversePathTree = traversePathTree; //# sourceMappingURL=pathTree.js.map