apollo-schema-extend
Version:
Extends your Apollo Server Express based graphql server with an external graphql source
156 lines (155 loc) • 7.08 kB
JavaScript
;
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFilteredDefinition = void 0;
const graphql_1 = require("graphql");
const ast_1 = require("../ast");
const definition_1 = require("../definition");
/**
* Filters the given operation based on the provided schema.
* @param operation The operation to be filtered.
* @param fragments Available fragments of the query
* @param schema The schema to be used for the filtering
*/
const getFilteredDefinition = (operation, resolveInfo, schema, remapRules) => {
const fragments = Object.entries(resolveInfo.fragments).reduce((total, next) => {
if (next[1]) {
const filteredFragment = getFilteredFragment(next[1], schema, remapRules, resolveInfo);
if (filteredFragment) {
total[next[0]] = filteredFragment;
}
}
return total;
}, {});
const filteredOperation = getFilteredOperation(operation, fragments, schema, remapRules, resolveInfo);
return { operation: filteredOperation, fragments };
};
exports.getFilteredDefinition = getFilteredDefinition;
const getFilteredOperation = (operation, fragments, schema, remapRules, resolveInfo) => {
const currentType = operation.operation === 'query' ? schema.getQueryType() : schema.getMutationType();
if (!currentType) {
throw new Error(`operation ${operation.operation} is not supported by the schema`);
}
return filterNode(operation, { currentType, schema, fragments, remapRules, resolveInfo });
};
const getFilteredFragment = (fragment, schema, remapRules, resolveInfo) => {
const currentType = schema.getType(fragment.typeCondition.name.value);
if (!currentType) {
return null;
}
return filterNode(fragment, { currentType, schema, fragments: resolveInfo.fragments, remapRules, resolveInfo });
};
const filterNode = (node, _a) => {
var { currentType } = _a, context = __rest(_a, ["currentType"]);
let filteredNode = node;
if ((0, graphql_1.isObjectType)(currentType) || (0, graphql_1.isInterfaceType)(currentType)) {
filteredNode = filterObjectLikeType(node, Object.assign({ currentType }, context));
}
else if ((0, graphql_1.isUnionType)(currentType)) {
filteredNode = filterUnionType(node, Object.assign({ currentType }, context));
}
if ((0, ast_1.isFieldNode)(node)) {
const remapRule = context.remapRules[currentType.name];
if (remapRule) {
filteredNode = remapRule(filteredNode, {
unfilteredNode: node,
resolveInfo: context.resolveInfo,
});
}
}
return filteredNode;
};
const typeNameKey = '__typename';
const isFragmentSpread = (0, ast_1.isNodeKind)(graphql_1.Kind.FRAGMENT_SPREAD);
const filterObjectLikeType = (node, _a) => {
var { currentType } = _a, context = __rest(_a, ["currentType"]);
if ((0, ast_1.hasSelectionSet)(node)) {
const fields = currentType.getFields();
const selections = [];
node.selectionSet.selections.forEach(selection => {
if ((0, ast_1.isFieldNode)(selection)) {
const field = fields[selection.name.value];
const selectionType = field && (0, definition_1.toNamedType)(field.type);
if (selectionType && isValidFieldSelection(selection, selectionType)) {
selections.push(filterNode(selection, Object.assign(Object.assign({}, context), { currentType: selectionType })));
}
else if (selection.name.value === typeNameKey) {
selections.push(selection);
}
}
else {
const filteredFragment = filterFragmentNode(selection, Object.assign({ currentType }, context));
if (filteredFragment) {
selections.push(filteredFragment);
}
}
});
if ((0, graphql_1.isInterfaceType)(currentType) || !selections.length) {
ensureTypename(selections);
}
return (0, ast_1.withSelections)(node, selections);
}
return node;
};
const isValidFieldSelection = (selection, selectionType) => selectionType instanceof graphql_1.GraphQLScalarType || selectionType instanceof graphql_1.GraphQLEnumType
? !selection.selectionSet
: !!selection.selectionSet;
const filterUnionType = (node, _a) => {
var { currentType } = _a, context = __rest(_a, ["currentType"]);
if ((0, ast_1.hasSelectionSet)(node)) {
const selections = [];
node.selectionSet.selections.forEach(selection => {
if ((0, ast_1.isFieldNode)(selection)) {
if (selection.name.value === typeNameKey) {
selections.push(selection);
}
}
else {
const filteredFragment = filterFragmentNode(selection, Object.assign({ currentType }, context));
if (filteredFragment) {
selections.push(selection);
}
}
});
ensureTypename(selections);
return (0, ast_1.withSelections)(node, selections);
}
return node;
};
const filterFragmentNode = (node, _a) => {
var { currentType } = _a, context = __rest(_a, ["currentType"]);
if (isFragmentSpread(node)) {
const fragment = context.fragments[node.name.value];
return fragment && isFragmentAllowed(fragment, currentType, context.schema) ? node : null;
}
if (isFragmentAllowed(node, currentType, context.schema)) {
const fragmentType = context.schema.getType(node.typeCondition.name.value);
const filteredNode = filterNode(node, Object.assign({ currentType: fragmentType }, context));
return filteredNode.selectionSet.selections.length ? filteredNode : null;
}
return null;
};
const isFragmentAllowed = (fragment, currentType, schema) => {
const fragmentType = fragment.typeCondition && schema.getType(fragment.typeCondition.name.value);
if (!fragmentType) {
return false;
}
return (currentType.name === fragmentType.name ||
((0, graphql_1.isAbstractType)(currentType) && schema.isSubType(currentType, fragmentType)) ||
((0, graphql_1.isAbstractType)(fragmentType) && schema.isSubType(fragmentType, currentType)));
};
const ensureTypename = (selections) => {
if (!selections.some(selection => (0, ast_1.isFieldNode)(selection) && selection.name.value === typeNameKey)) {
selections.push((0, ast_1.createField)(typeNameKey));
}
};