import { GraphQLResolveInfo, SelectionSetNode, Kind, SelectionNode, FieldNode, InlineFragmentNode, ValueNode, } from 'graphql'; const isFieldNode = (node: SelectionNode): node is FieldNode => node.kind === Kind.FIELD; const isInlineFragmentNode = ( node: SelectionNode, ): node is InlineFragmentNode => node.kind === Kind.INLINE_FRAGMENT; const findFieldNode = ( selectionSet: SelectionSetNode | undefined, key: string, ): FieldNode | null => { if (!selectionSet) return null; let field: FieldNode | null = null; for (const selection of selectionSet.selections) { // We've found the field if (isFieldNode(selection) && selection.name.value === key) { return selection; } // Recursively search for the field in nested selections if ( (isFieldNode(selection) || isInlineFragmentNode(selection)) && selection.selectionSet ) { field = findFieldNode(selection.selectionSet, key); // If we find the field in a nested selection, stop searching if (field) break; } } return field; }; const parseValueNode = ( valueNode: ValueNode, variables: GraphQLResolveInfo['variableValues'], ) => { switch (valueNode.kind) { case Kind.VARIABLE: return variables[valueNode.name.value]; case Kind.INT: case Kind.FLOAT: return Number(valueNode.value); case Kind.STRING: case Kind.BOOLEAN: case Kind.ENUM: return valueNode.value; case Kind.LIST: return valueNode.values.map((value) => parseValueNode(value, variables)); case Kind.OBJECT: return valueNode.fields.reduce((obj, field) => { obj[field.name.value] = parseValueNode(field.value, variables); return obj; }, {}); default: return null; } }; export const getFieldArgumentsByKey = ( info: GraphQLResolveInfo, fieldKey: string, ): Record => { // Start from the first top-level field node and search recursively const targetField = findFieldNode(info.fieldNodes[0].selectionSet, fieldKey); // If the field is not found, throw an error if (!targetField) { throw new Error(`Field "${fieldKey}" not found.`); } // Extract the arguments from the field we've found const args: Record = {}; if (targetField.arguments && targetField.arguments.length) { for (const arg of targetField.arguments) { args[arg.name.value] = parseValueNode(arg.value, info.variableValues); } } return args; };