UNPKG

@adpt/core

Version:
177 lines 7.17 kB
"use strict"; /* * Copyright 2018-2019 Unbounded Systems, LLC * * 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. */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const graphql_1 = require("graphql"); const ld = tslib_1.__importStar(require("lodash")); const error_1 = require("../error"); function notUndefined(x) { return x !== undefined; } function getUnwrappedType(type) { while (graphql_1.isWrappingType(type)) type = type.ofType; return type; } function buildSelectionSet(type, orig, allDepth = 1) { if (allDepth === 0) return orig; type = getUnwrappedType(type); if (!graphql_1.isObjectType(type)) return orig; //FIXME(manishv) handle interfaces and fragments spreads const origNames = orig ? orig.selections.map((f) => { if (f.kind !== graphql_1.Kind.FIELD) return undefined; //FIXME(manishv) Need to look into fragment refs here return f.alias ? f.alias.value : f.name.value; }).filter((x) => x !== undefined) : []; const origSet = new Set(origNames); const fields = type.getFields(); const newSelectionNamesLessOrig = Object.keys(fields).filter((n) => !origSet.has(n)); const newSelectionsLessOrig = newSelectionNamesLessOrig.map((name) => { const field = fields[name]; if (!needsNoArgs(field)) return undefined; const selectionSet = buildSelectionSet(field.type, undefined, allDepth - 1); return { kind: graphql_1.Kind.FIELD, name: { kind: graphql_1.Kind.NAME, value: name }, selectionSet }; }).filter(notUndefined); const finalSelections = orig ? orig.selections.concat(newSelectionsLessOrig) : newSelectionsLessOrig; return { kind: graphql_1.Kind.SELECTION_SET, loc: orig ? orig.loc : undefined, selections: finalSelections }; } function needsNoArgs(f) { if (!f.args) return true; if (f.args.length === 0) return true; const noDefaultArgs = f.args.filter((arg) => arg.defaultValue === undefined); if (noDefaultArgs.length === 0) return true; const nonNullArgs = f.args.filter((arg) => graphql_1.isNonNullType(arg.type)); return nonNullArgs.length === 0; } function findAllDepth(n) { const dirs = n.directives; if (dirs === undefined) return 0; const alls = dirs.filter((d) => d.name.value === "all"); if (alls.length === 0) return 0; const depths = alls.map((all) => { const args = all.arguments; if (!args || args.length === 0) return 1; const depthArgs = args.filter((a) => a.name.value === "depth"); const depthValues = depthArgs.map((da) => { const valNode = da.value; if (valNode.kind !== graphql_1.Kind.INT) throw new Error("@all has a non-integer depth argument"); const ret = Number(valNode.value); if (isNaN(ret)) throw new Error("@all has a non-integer depth argument"); if (ret < 0) throw new Error("@all has depth < 0"); return ret; }); return Math.max(0, ...depthValues); }); return Math.max(0, ...depths); } class AllDirectiveVisitor { constructor(schema) { this.schema = schema; this.leave = { OperationDefinition: (op) => this.processFieldOrOpNode(op), Field: (f) => this.processFieldOrOpNode(f) }; this.enter = { OperationDefinition: (op) => { const allDepth = findAllDepth(op); switch (op.operation) { case "query": this.infoStack = [{ type: this.schema.getQueryType() || null, allDepth }]; break; case "mutation": this.infoStack = [{ type: this.schema.getMutationType() || null, allDepth }]; break; case "subscription": this.infoStack = [{ type: this.schema.getSubscriptionType() || null, allDepth }]; } }, Field: (f) => { const fieldName = f.name.value; const info = ld.last(this.infoStack); if (info === undefined) throw new error_1.InternalError(`no info for field: ${fieldName}`); const type = info.type; if (type === undefined) throw new error_1.InternalError(`no type for field: ${fieldName}`); if (!graphql_1.isObjectType(type)) return; //FIXME(manishv) Fix fragment spreads and interfaces here const allDepth = Math.max(findAllDepth(f), (info.allDepth - 1)); if (type === null) { this.infoStack.push({ type: null, allDepth }); return; } const fields = type.getFields(); const field = fields[fieldName]; if (field === undefined) { this.infoStack.push({ type: null, allDepth }); return; } this.infoStack.push({ type: getUnwrappedType(field.type), allDepth }); } }; this.processFieldOrOpNode = (n) => { const info = this.infoStack.pop(); if (!info) return n; const type = info.type; if (!graphql_1.isObjectType(type)) return n; if (info.allDepth === 0) return n; const origSel = n.selectionSet; const sel = buildSelectionSet(type, origSel, info.allDepth); if (n.kind === graphql_1.Kind.OPERATION_DEFINITION) { if (sel === undefined) { throw new Error("Cannot have empty selection set at top-level operation: " + n.operation); } return Object.assign({}, n, { selectionSet: sel }); } return Object.assign({}, n, { selectionSet: sel }); }; } } function applyAdaptTransforms(schema, q) { return graphql_1.visit(q, new AllDirectiveVisitor(schema)); } exports.applyAdaptTransforms = applyAdaptTransforms; function adaptGqlExecute(schema, query, data, context, vars) { const tQuery = applyAdaptTransforms(schema, query); return graphql_1.execute(schema, tQuery, data, context, vars); } exports.adaptGqlExecute = adaptGqlExecute; //# sourceMappingURL=query_transforms.js.map