Files
Home_Buying_App/node_modules/@apollo/server/dist/esm/requestPipeline.js
jackbeeby b412dfe2ca Initialisation
Added the packages and files for the backend server
2024-12-15 17:48:45 +11:00

310 lines
14 KiB
JavaScript

import { createHash } from '@apollo/utils.createhash';
import { specifiedRules, getOperationAST, GraphQLError, validate, parse, Kind, } from 'graphql';
import { symbolExecutionDispatcherWillResolveField, enablePluginsForSchemaResolvers, symbolUserFieldResolver, } from './utils/schemaInstrumentation.js';
import { PersistedQueryNotSupportedError, PersistedQueryNotFoundError, UserInputError, BadRequestError, ValidationError, SyntaxError, OperationResolutionError, } from './internalErrorClasses.js';
import { ensureError, normalizeAndFormatErrors, ensureGraphQLError, } from './errorNormalize.js';
import { invokeDidStartHook, invokeHooksUntilDefinedAndNonNull, invokeSyncDidStartHook, } from './utils/invokeHooks.js';
import { makeGatewayGraphQLRequestContext } from './utils/makeGatewayGraphQLRequestContext.js';
import { mergeHTTPGraphQLHead, newHTTPGraphQLHead } from './runHttpQuery.js';
import { isDefined } from './utils/isDefined.js';
import { executeIncrementally, } from './incrementalDeliveryPolyfill.js';
import { HeaderMap } from './utils/HeaderMap.js';
export const APQ_CACHE_PREFIX = 'apq:';
function computeQueryHash(query) {
return createHash('sha256').update(query).digest('hex');
}
function isBadUserInputGraphQLError(error) {
return (error.nodes?.length === 1 &&
error.nodes[0].kind === Kind.VARIABLE_DEFINITION &&
(error.message.startsWith(`Variable "$${error.nodes[0].variable.name.value}" got invalid value `) ||
error.message.startsWith(`Variable "$${error.nodes[0].variable.name.value}" of required type `) ||
error.message.startsWith(`Variable "$${error.nodes[0].variable.name.value}" of non-null type `)));
}
export async function processGraphQLRequest(schemaDerivedData, server, internals, requestContext) {
const requestListeners = (await Promise.all(internals.plugins.map((p) => p.requestDidStart?.(requestContext)))).filter(isDefined);
const request = requestContext.request;
let { query, extensions } = request;
let queryHash;
requestContext.metrics.persistedQueryHit = false;
requestContext.metrics.persistedQueryRegister = false;
if (extensions?.persistedQuery) {
if (!internals.persistedQueries) {
return await sendErrorResponse([new PersistedQueryNotSupportedError()]);
}
else if (extensions.persistedQuery.version !== 1) {
return await sendErrorResponse([
new GraphQLError('Unsupported persisted query version', {
extensions: { http: newHTTPGraphQLHead(400) },
}),
]);
}
queryHash = extensions.persistedQuery.sha256Hash;
if (query === undefined) {
query = await internals.persistedQueries.cache.get(queryHash);
if (query) {
requestContext.metrics.persistedQueryHit = true;
}
else {
return await sendErrorResponse([new PersistedQueryNotFoundError()]);
}
}
else {
const computedQueryHash = computeQueryHash(query);
if (queryHash !== computedQueryHash) {
return await sendErrorResponse([
new GraphQLError('provided sha does not match query', {
extensions: { http: newHTTPGraphQLHead(400) },
}),
]);
}
requestContext.metrics.persistedQueryRegister = true;
}
}
else if (query) {
queryHash = computeQueryHash(query);
}
else {
return await sendErrorResponse([
new BadRequestError('GraphQL operations must contain a non-empty `query` or a `persistedQuery` extension.'),
]);
}
requestContext.queryHash = queryHash;
requestContext.source = query;
await Promise.all(requestListeners.map((l) => l.didResolveSource?.(requestContext)));
if (schemaDerivedData.documentStore) {
try {
requestContext.document = await schemaDerivedData.documentStore.get(schemaDerivedData.documentStoreKeyPrefix + queryHash);
}
catch (err) {
server.logger.warn('An error occurred while attempting to read from the documentStore. ' +
ensureError(err).message);
}
}
if (!requestContext.document) {
const parsingDidEnd = await invokeDidStartHook(requestListeners, async (l) => l.parsingDidStart?.(requestContext));
try {
requestContext.document = parse(query, internals.parseOptions);
}
catch (syntaxMaybeError) {
const error = ensureError(syntaxMaybeError);
await parsingDidEnd(error);
return await sendErrorResponse([
new SyntaxError(ensureGraphQLError(error)),
]);
}
await parsingDidEnd();
if (internals.dangerouslyDisableValidation !== true) {
const validationDidEnd = await invokeDidStartHook(requestListeners, async (l) => l.validationDidStart?.(requestContext));
const validationErrors = validate(schemaDerivedData.schema, requestContext.document, [...specifiedRules, ...internals.validationRules]);
if (validationErrors.length === 0) {
await validationDidEnd();
}
else {
await validationDidEnd(validationErrors);
return await sendErrorResponse(validationErrors.map((error) => new ValidationError(error)));
}
}
if (schemaDerivedData.documentStore) {
Promise.resolve(schemaDerivedData.documentStore.set(schemaDerivedData.documentStoreKeyPrefix + queryHash, requestContext.document)).catch((err) => server.logger.warn('Could not store validated document. ' + err?.message || err));
}
}
const operation = getOperationAST(requestContext.document, request.operationName);
requestContext.operation = operation || undefined;
requestContext.operationName = operation?.name?.value || null;
if (request.http?.method === 'GET' &&
operation?.operation &&
operation.operation !== 'query') {
return await sendErrorResponse([
new BadRequestError(`GET requests only support query operations, not ${operation.operation} operations`, {
extensions: {
http: { status: 405, headers: new HeaderMap([['allow', 'POST']]) },
},
}),
]);
}
try {
await Promise.all(requestListeners.map((l) => l.didResolveOperation?.(requestContext)));
}
catch (err) {
return await sendErrorResponse([ensureGraphQLError(err)]);
}
if (requestContext.metrics.persistedQueryRegister &&
internals.persistedQueries) {
const ttl = internals.persistedQueries?.ttl;
Promise.resolve(internals.persistedQueries.cache.set(queryHash, query, ttl !== undefined
? { ttl: internals.persistedQueries?.ttl }
: undefined)).catch(server.logger.warn);
}
const responseFromPlugin = await invokeHooksUntilDefinedAndNonNull(requestListeners, async (l) => await l.responseForOperation?.(requestContext));
if (responseFromPlugin !== null) {
requestContext.response.body = responseFromPlugin.body;
mergeHTTPGraphQLHead(requestContext.response.http, responseFromPlugin.http);
}
else {
const executionListeners = (await Promise.all(requestListeners.map((l) => l.executionDidStart?.(requestContext)))).filter(isDefined);
executionListeners.reverse();
if (executionListeners.some((l) => l.willResolveField)) {
const invokeWillResolveField = (...args) => invokeSyncDidStartHook(executionListeners, (l) => l.willResolveField?.(...args));
Object.defineProperty(requestContext.contextValue, symbolExecutionDispatcherWillResolveField, { value: invokeWillResolveField });
if (internals.fieldResolver) {
Object.defineProperty(requestContext.contextValue, symbolUserFieldResolver, {
value: internals.fieldResolver,
});
}
enablePluginsForSchemaResolvers(schemaDerivedData.schema);
}
try {
const fullResult = await execute(requestContext);
const result = 'singleResult' in fullResult
? fullResult.singleResult
: fullResult.initialResult;
if (!requestContext.operation) {
if (!result.errors?.length) {
throw new Error('Unexpected error: Apollo Server did not resolve an operation but execute did not return errors');
}
throw new OperationResolutionError(result.errors[0]);
}
const resultErrors = result.errors?.map((e) => {
if (isBadUserInputGraphQLError(e) && e.extensions?.code == null) {
return new UserInputError(e);
}
return e;
});
if (resultErrors) {
await didEncounterErrors(resultErrors);
}
const { formattedErrors, httpFromErrors } = resultErrors
? formatErrors(resultErrors)
: { formattedErrors: undefined, httpFromErrors: newHTTPGraphQLHead() };
if (internals.status400ForVariableCoercionErrors &&
resultErrors?.length &&
result.data === undefined &&
!httpFromErrors.status) {
httpFromErrors.status = 400;
}
mergeHTTPGraphQLHead(requestContext.response.http, httpFromErrors);
if ('singleResult' in fullResult) {
requestContext.response.body = {
kind: 'single',
singleResult: {
...result,
errors: formattedErrors,
},
};
}
else {
requestContext.response.body = {
kind: 'incremental',
initialResult: {
...fullResult.initialResult,
errors: formattedErrors,
},
subsequentResults: fullResult.subsequentResults,
};
}
}
catch (executionMaybeError) {
const executionError = ensureError(executionMaybeError);
await Promise.all(executionListeners.map((l) => l.executionDidEnd?.(executionError)));
return await sendErrorResponse([ensureGraphQLError(executionError)]);
}
await Promise.all(executionListeners.map((l) => l.executionDidEnd?.()));
}
await invokeWillSendResponse();
if (!requestContext.response.body) {
throw Error('got to end of processGraphQLRequest without setting body?');
}
return requestContext.response;
async function execute(requestContext) {
const { request, document } = requestContext;
if (internals.__testing_incrementalExecutionResults) {
return internals.__testing_incrementalExecutionResults;
}
else if (internals.gatewayExecutor) {
const result = await internals.gatewayExecutor(makeGatewayGraphQLRequestContext(requestContext, server, internals));
return { singleResult: result };
}
else {
const resultOrResults = await executeIncrementally({
schema: schemaDerivedData.schema,
document,
rootValue: typeof internals.rootValue === 'function'
? internals.rootValue(document)
: internals.rootValue,
contextValue: requestContext.contextValue,
variableValues: request.variables,
operationName: request.operationName,
fieldResolver: internals.fieldResolver,
});
if ('initialResult' in resultOrResults) {
return {
initialResult: resultOrResults.initialResult,
subsequentResults: formatErrorsInSubsequentResults(resultOrResults.subsequentResults),
};
}
else {
return { singleResult: resultOrResults };
}
}
}
async function* formatErrorsInSubsequentResults(results) {
for await (const result of results) {
const payload = result.incremental
? {
...result,
incremental: await seriesAsyncMap(result.incremental, async (incrementalResult) => {
const { errors } = incrementalResult;
if (errors) {
await Promise.all(requestListeners.map((l) => l.didEncounterSubsequentErrors?.(requestContext, errors)));
return {
...incrementalResult,
errors: formatErrors(errors).formattedErrors,
};
}
return incrementalResult;
}),
}
: result;
await Promise.all(requestListeners.map((l) => l.willSendSubsequentPayload?.(requestContext, payload)));
yield payload;
}
}
async function invokeWillSendResponse() {
await Promise.all(requestListeners.map((l) => l.willSendResponse?.(requestContext)));
}
async function didEncounterErrors(errors) {
requestContext.errors = errors;
return await Promise.all(requestListeners.map((l) => l.didEncounterErrors?.(requestContext)));
}
async function sendErrorResponse(errors) {
await didEncounterErrors(errors);
const { formattedErrors, httpFromErrors } = formatErrors(errors);
requestContext.response.body = {
kind: 'single',
singleResult: {
errors: formattedErrors,
},
};
mergeHTTPGraphQLHead(requestContext.response.http, httpFromErrors);
if (!requestContext.response.http.status) {
requestContext.response.http.status = 500;
}
await invokeWillSendResponse();
return requestContext.response;
}
function formatErrors(errors) {
return normalizeAndFormatErrors(errors, {
formatError: internals.formatError,
includeStacktraceInErrorResponses: internals.includeStacktraceInErrorResponses,
});
}
}
async function seriesAsyncMap(ts, fn) {
const us = [];
for (const t of ts) {
const u = await fn(t);
us.push(u);
}
return us;
}
//# sourceMappingURL=requestPipeline.js.map