UNPKG

yekonga-server

Version:
137 lines (123 loc) 4.31 kB
'use strict'; const options = {}; function getSelections(ast) { if (ast && ast.selectionSet && ast.selectionSet.selections && ast.selectionSet.selections.length) { return ast.selectionSet.selections; } return []; } function isFragment(ast) { return ast.kind === 'InlineFragment' || ast.kind === 'FragmentSpread'; } function getAST(ast, info) { if (ast.kind === 'FragmentSpread') { const fragmentName = ast.name.value; return info.fragments[fragmentName]; } return ast; } function getArguments(ast, info) { var params = {}; for (const argument of ast.arguments) { const argumentValue = getArgumentValue(argument.value, info); params[argument.name.value] = { kind: argument.value.kind, value: argumentValue }; } return params } function getArgumentValue(arg, info) { switch (arg.kind) { case 'FloatValue': return parseFloat(arg.value); case 'IntValue': return parseInt(arg.value, 10); case 'Variable': return info.variableValues[arg.name.value]; case 'ListValue': return arg.values.map(argument => getArgumentValue(argument, info)); case 'ObjectValue': return arg.fields.reduce( (argValue, objectField) => { argValue[objectField.name.value] = getArgumentValue(objectField.value, info); return argValue; }, {}, ); default: return arg.value; } } function getDirectiveValue(directive, info) { const arg = directive.arguments[0]; // only arg on an include or skip directive is "if" if (arg.value.kind !== "Variable") { return !!arg.value.value; } return info.variableValues[arg.value.name.value]; } function getDirectiveResults(ast, info) { const directiveResult = { shouldInclude: true, shouldSkip: false, }; return ast.directives.reduce((result, directive) => { switch (directive.name.value) { case "include": return {...result, shouldInclude: getDirectiveValue(directive, info) }; case "skip": return {...result, shouldSkip: getDirectiveValue(directive, info) }; default: return result; } }, directiveResult); } function flattenAST(ast, info, obj) { obj = obj || {}; return getSelections(ast).reduce((flattened, a) => { if (a.directives && a.directives.length) { const { shouldInclude, shouldSkip } = getDirectiveResults(a, info); // field/fragment is not included if either the @skip condition is true or the @include condition is false // https://facebook.github.io/graphql/draft/#sec--include if (shouldSkip || !shouldInclude) { return flattened; } } if (isFragment(a)) { flattened = flattenAST(getAST(a, info), info, flattened); } else { const name = a.name.value; if (options.excludedFields.indexOf(name) !== -1) { return flattened; } if (flattened[name] && flattened[name] !== '__arguments') { Object.assign(flattened[name], flattenAST(a, info, flattened[name])); } else { flattened[name] = flattenAST(a, info); } if (options.processArguments) { // check if the current field has arguments if (a.arguments && a.arguments.length) { Object.assign(flattened[name], { params: getArguments(a, info) }); } } } return flattened; }, obj); } module.exports = function graphqlFields(info, obj = {}, opts = { processArguments: true }) { if (!info) info = {} try { const fields = info.fieldNodes || info.fieldASTs; options.processArguments = opts.processArguments; options.excludedFields = opts.excludedFields || []; return fields.reduce((o, ast) => { return flattenAST(ast, info, o); }, obj) || {}; } catch (error) { console.error(error); } return {} };