UNPKG

@apollo/gateway

Version:
229 lines 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeResponse = void 0; const federation_internals_1 = require("@apollo/federation-internals"); function computeResponse({ operation, variables, input, introspectionHandling, }) { if (!input) { return { data: input, errors: [] }; } const parameters = { schema: operation.schema(), variables: { ...operation.collectDefaultedVariableValues(), ...variables, }, errors: [], introspectionHandling, }; const data = Object.create(null); const res = applySelectionSet({ input, selectionSet: operation.selectionSet, output: data, parameters, path: [], parentType: operation.schema().schemaDefinition.rootType(operation.rootKind), }); return { data: res === ApplyResult.NULL_BUBBLE_UP ? null : data, errors: parameters.errors, }; } exports.computeResponse = computeResponse; function shouldSkip(element, parameters) { const skipDirective = element.appliedDirectivesOf(parameters.schema.skipDirective())[0]; const includeDirective = element.appliedDirectivesOf(parameters.schema.includeDirective())[0]; return (skipDirective && ifValue(skipDirective, parameters.variables)) || (includeDirective && !ifValue(includeDirective, parameters.variables)); } function ifValue(directive, variables) { const ifArg = directive.arguments().if; if (ifArg instanceof federation_internals_1.Variable) { const value = variables[ifArg.name]; (0, federation_internals_1.assert)(value !== undefined && typeof value === 'boolean', () => `Unexpected value ${value} for variable ${ifArg} of ${directive}`); return value; } else { return ifArg; } } var ApplyResult; (function (ApplyResult) { ApplyResult[ApplyResult["OK"] = 0] = "OK"; ApplyResult[ApplyResult["NULL_BUBBLE_UP"] = 1] = "NULL_BUBBLE_UP"; })(ApplyResult || (ApplyResult = {})); function typeConditionApplies(schema, typeCondition, typename, parentType) { if (!typeCondition) { return true; } if (typename) { const type = schema.type(typename); return !!type && (0, federation_internals_1.isSubtype)(typeCondition, type); } else { return (0, federation_internals_1.isSubtype)(typeCondition, parentType); } } function applySelectionSet({ input, selectionSet, output, parameters, path, parentType, }) { var _a, _b, _c, _d; for (const selection of selectionSet.selections()) { if (shouldSkip(selection.element, parameters)) { continue; } if (selection.kind === 'FieldSelection') { const field = selection.element; const fieldType = field.definition.type; const responseName = field.responseName(); const outputValue = output[responseName]; if (field.definition.isSchemaIntrospectionField()) { if (outputValue === undefined) { output[responseName] = parameters.introspectionHandling(selection); } continue; } let inputValue = (_a = input[responseName]) !== null && _a !== void 0 ? _a : null; if (field.name === federation_internals_1.typenameFieldName) { if (outputValue === undefined) { if (inputValue === null && responseName !== federation_internals_1.typenameFieldName) { inputValue = (_b = input[federation_internals_1.typenameFieldName]) !== null && _b !== void 0 ? _b : null; } const type = inputValue !== null && typeof inputValue === 'string' ? (_c = parameters.schema.type(inputValue)) !== null && _c !== void 0 ? _c : parentType : parentType; if (!(0, federation_internals_1.isObjectType)(type)) { return ApplyResult.NULL_BUBBLE_UP; } output[responseName] = type.name; } continue; } path.push(responseName); const { updated, isInvalid } = updateOutputValue({ outputValue, type: fieldType, inputValue, selectionSet: selection.selectionSet, path, parameters, parentType, }); output[responseName] = updated; path.pop(); if (isInvalid) { return ApplyResult.NULL_BUBBLE_UP; } } else { const fragment = selection.element; const typename = input[federation_internals_1.typenameFieldName]; (0, federation_internals_1.assert)(!typename || typeof typename === 'string', () => `Got unexpected value for __typename: ${typename}`); if (typeConditionApplies(parameters.schema, fragment.typeCondition, typename, parentType)) { const res = applySelectionSet({ input, selectionSet: selection.selectionSet, output, parameters, path, parentType: (_d = fragment.typeCondition) !== null && _d !== void 0 ? _d : parentType, }); if (res === ApplyResult.NULL_BUBBLE_UP) { return ApplyResult.NULL_BUBBLE_UP; } } } } return ApplyResult.OK; } function pathLastElementDescription(path, currentType, parentType) { const element = path[path.length - 1]; (0, federation_internals_1.assert)(element !== undefined, 'Should not have been called on an empty path'); return typeof element === 'string' ? `field ${parentType}.${element}` : `array element of type ${currentType} at index ${element}`; } function updateOutputValue({ outputValue, type, inputValue, selectionSet, path, parameters, parentType, }) { (0, federation_internals_1.assert)(inputValue !== undefined, 'Should not pass undefined for `inputValue` to this method'); if (outputValue === null || (outputValue !== undefined && !selectionSet)) { return { updated: outputValue }; } if ((0, federation_internals_1.isNonNullType)(type)) { const { updated, hasErrors } = updateOutputValue({ outputValue, type: type.ofType, inputValue, selectionSet, path, parameters, parentType, }); if (updated === null) { if (!hasErrors) { parameters.errors.push(federation_internals_1.ERRORS.INVALID_GRAPHQL.err(`Cannot return null for non-nullable ${pathLastElementDescription(path, type.ofType, parentType)}.`, { path: Array.from(path) })); } return { updated, isInvalid: true, hasErrors: true }; } return { updated }; } if (inputValue === null) { return { updated: null }; } if ((0, federation_internals_1.isListType)(type)) { (0, federation_internals_1.assert)(Array.isArray(inputValue), () => `Invalid non-list value ${inputValue} returned by subgraph for list type ${type}`); (0, federation_internals_1.assert)(outputValue === undefined || Array.isArray(outputValue), () => `Invalid non-list value ${outputValue} returned by subgraph for list type ${type}`); const outputValueList = outputValue === undefined ? new Array(inputValue.length).fill(undefined) : outputValue; (0, federation_internals_1.assert)(inputValue.length === outputValueList.length, () => `[${inputValue}].length (${inputValue.length}) !== [${outputValueList}].length (${outputValueList.length})`); let shouldNullify = false; let hasErrors = false; const updated = outputValueList.map((outputEltValue, idx) => { path.push(idx); const elt = updateOutputValue({ outputValue: outputEltValue, type: type.ofType, inputValue: inputValue[idx], selectionSet, path, parameters, parentType, }); path.pop(); shouldNullify || (shouldNullify = !!elt.isInvalid); hasErrors || (hasErrors = !!elt.hasErrors); return elt.updated; }); return { updated: shouldNullify ? null : updated, hasErrors }; } if ((0, federation_internals_1.isCompositeType)(type)) { (0, federation_internals_1.assert)(selectionSet, () => `Invalid empty selection set for composite type ${type}`); (0, federation_internals_1.assert)(typeof inputValue === 'object', () => `Invalid non-object value ${inputValue} returned by subgraph for composite type ${type}`); (0, federation_internals_1.assert)(outputValue === undefined || typeof outputValue === 'object', () => `Invalid non-object value ${inputValue} returned by subgraph for composite type ${type}`); const inputTypename = inputValue[federation_internals_1.typenameFieldName]; (0, federation_internals_1.assert)(inputTypename === undefined || typeof inputTypename === 'string', () => `Invalid non-string value ${inputTypename} for __typename at ${path}`); let objType = type; if (inputTypename) { const typenameType = parameters.schema.type(inputTypename); if (!typenameType || !(0, federation_internals_1.isCompositeType)(typenameType)) { parameters.errors.push(federation_internals_1.ERRORS.INVALID_GRAPHQL.err(`Invalid __typename found for object at ${pathLastElementDescription(path, type, parentType)}.`, { path: Array.from(path) })); return { updated: null, hasErrors: true }; } objType = typenameType; } const outputValueObject = outputValue === undefined ? Object.create(null) : outputValue; const res = applySelectionSet({ input: inputValue, selectionSet, output: outputValueObject, parameters, path, parentType: objType, }); const hasErrors = res === ApplyResult.NULL_BUBBLE_UP; return { updated: hasErrors ? null : outputValueObject, hasErrors }; } (0, federation_internals_1.assert)(outputValue === undefined, () => `Excepted output to be undefined but got ${type} for type ${type}`); const isValidValue = (0, federation_internals_1.isValidLeafValue)(parameters.schema, inputValue, type); if (!isValidValue) { parameters.errors.push(federation_internals_1.ERRORS.INVALID_GRAPHQL.err(`Invalid value found for ${pathLastElementDescription(path, type, parentType)}.`, { path: Array.from(path) })); } return { updated: isValidValue ? inputValue : null, hasErrors: !isValidValue }; } //# sourceMappingURL=resultShaping.js.map