1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | 40x 20x 20x 20x 20x 32x 20x 64x 64x 107x 2x 12x 93x 93x 93x 93x 93x 93x 12x 8x 4x 4x 12x 12x 12x 64x 64x 64x 93x 93x 93x 93x 93x 66x 27x 27x 8x 19x 10x 15x 15x 2x 13x | import { DocumentNode, SelectionSetNode, FieldNode, FragmentDefinitionNode, InlineFragmentNode, } from 'graphql'; import { getMainDefinition, getFragmentDefinitions, createFragmentMap, shouldInclude, getDirectiveInfoFromField, isField, isInlineFragment, resultKeyNameFromField, argumentsObjectFromField, } from 'apollo-utilities'; import { merge, Resolver, VariableMap, ExecContext, ExecInfo, ExecOptions, } from './graphql'; /* Based on graphql function from graphql-js: * * graphql( * schema: GraphQLSchema, * requestString: string, * rootValue?: ?any, * contextValue?: ?any, * variableValues?: ?{[key: string]: any}, * operationName?: ?string * ): Promise<GraphQLResult> * * The default export as of graphql-anywhere is sync as of 4.0, * but below is an exported alternative that is async. * In the 5.0 version, this will be the only export again * and it will be async * */ export function graphql( resolver: Resolver, document: DocumentNode, rootValue?: any, contextValue?: any, variableValues?: VariableMap, EexecOptions: ExecOptions = {}, ): Promise<null | Object> { const mainDefinition = getMainDefinition(document); const fragments = getFragmentDefinitions(document); const fragmentMap = createFragmentMap(fragments); const resultMapper = execOptions.resultMapper; // Default matcher always matches all fragments const fragmentMatcher = execOptions.fragmentMatcher || (() => true); const execContext: ExecContext = { fragmentMap, contextValue, variableValues, resultMapper, resolver, fragmentMatcher, }; return executeSelectionSet( mainDefinition.selectionSet, rootValue, execContext, ); } async function executeSelectionSet( selectionSet: SelectionSetNode, rootValue: any, execContext: ExecContext, ) { const { fragmentMap, contextValue, variableValues: variables } = execContext; const result = {}; const execute = async selection => { if (!shouldInclude(selection, variables)) { // Skip this entirely return; } if (isField(selection)) { const fieldResult = await executeField(selection, rootValue, execContext); const resultFieldKey = resultKeyNameFromField(selection); Eif (fieldResult !== undefined) { Eif (result[resultFieldKey] === undefined) { result[resultFieldKey] = fieldResult; } else { merge(result[resultFieldKey], fieldResult); } } return; } let fragment: InlineFragmentNode | FragmentDefinitionNode; if (isInlineFragment(selection)) { fragment = selection; } else { // This is a named fragment fragment = fragmentMap[selection.name.value]; Iif (!fragment) { throw new Error(`No fragment named ${selection.name.value}`); } } const typeCondition = fragment.typeCondition.name.value; if (execContext.fragmentMatcher(rootValue, typeCondition, contextValue)) { const fragmentResult = await executeSelectionSet( fragment.selectionSet, rootValue, execContext, ); merge(result, fragmentResult); } }; await Promise.all(selectionSet.selections.map(execute)); Iif (execContext.resultMapper) { return execContext.resultMapper(result, rootValue); } return result; } async function executeField( field: FieldNode, rootValue: any, execContext: ExecContext, ): Promise<null | Object> { const { variableValues: variables, contextValue, resolver } = execContext; const fieldName = field.name.value; const args = argumentsObjectFromField(field, variables); const info: ExecInfo = { isLeaf: !field.selectionSet, resultKey: resultKeyNameFromField(field), directives: getDirectiveInfoFromField(field, variables), }; const result = await resolver(fieldName, rootValue, args, contextValue, info); // Handle all scalar types here if (!field.selectionSet) { return result; } // From here down, the field has a selection set, which means it's trying to // query a GraphQLObjectType Iif (result == null) { // Basically any field in a GraphQL response can be null, or missing return result; } if (Array.isArray(result)) { return executeSubSelectedArray(field, result, execContext); } // Returned value is an object, and the query has a sub-selection. Recurse. return executeSelectionSet(field.selectionSet, result, execContext); } function executeSubSelectedArray(field, result, execContext) { return Promise.all( result.map(item => { // null value in array Iif (item === null) { return null; } // This is a nested array, recurse if (Array.isArray(item)) { return executeSubSelectedArray(field, item, execContext); } // This is an object, run the selection set on it return executeSelectionSet(field.selectionSet, item, execContext); }), ); } |