@apollo/gateway
Version:
229 lines • 11.1 kB
JavaScript
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
;