"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mergeHTTPGraphQLHead = exports.newHTTPGraphQLHead = exports.prettyJSONStringify = exports.runHttpQuery = void 0; const ApolloServer_js_1 = require("./ApolloServer.js"); const graphql_1 = require("graphql"); const internalErrorClasses_js_1 = require("./internalErrorClasses.js"); const negotiator_1 = __importDefault(require("negotiator")); const HeaderMap_js_1 = require("./utils/HeaderMap.js"); function fieldIfString(o, fieldName) { const value = o[fieldName]; if (typeof value === 'string') { return value; } return undefined; } function searchParamIfSpecifiedOnce(searchParams, paramName) { const values = searchParams.getAll(paramName); switch (values.length) { case 0: return undefined; case 1: return values[0]; default: throw new internalErrorClasses_js_1.BadRequestError(`The '${paramName}' search parameter may only be specified once.`); } } function jsonParsedSearchParamIfSpecifiedOnce(searchParams, fieldName) { const value = searchParamIfSpecifiedOnce(searchParams, fieldName); if (value === undefined) { return undefined; } let hopefullyRecord; try { hopefullyRecord = JSON.parse(value); } catch { throw new internalErrorClasses_js_1.BadRequestError(`The ${fieldName} search parameter contains invalid JSON.`); } if (!isStringRecord(hopefullyRecord)) { throw new internalErrorClasses_js_1.BadRequestError(`The ${fieldName} search parameter should contain a JSON-encoded object.`); } return hopefullyRecord; } function fieldIfRecord(o, fieldName) { const value = o[fieldName]; if (isStringRecord(value)) { return value; } return undefined; } function isStringRecord(o) { return (!!o && typeof o === 'object' && !Buffer.isBuffer(o) && !Array.isArray(o)); } function isNonEmptyStringRecord(o) { return isStringRecord(o) && Object.keys(o).length > 0; } function ensureQueryIsStringOrMissing(query) { if (!query || typeof query === 'string') { return; } if (query.kind === graphql_1.Kind.DOCUMENT) { throw new internalErrorClasses_js_1.BadRequestError("GraphQL queries must be strings. It looks like you're sending the " + 'internal graphql-js representation of a parsed query in your ' + 'request instead of a request in the GraphQL query language. You ' + 'can convert an AST to a string using the `print` function from ' + '`graphql`, or use a client like `apollo-client` which converts ' + 'the internal representation to a string for you.'); } else { throw new internalErrorClasses_js_1.BadRequestError('GraphQL queries must be strings.'); } } async function runHttpQuery({ server, httpRequest, contextValue, schemaDerivedData, internals, sharedResponseHTTPGraphQLHead, }) { let graphQLRequest; switch (httpRequest.method) { case 'POST': { if (!isNonEmptyStringRecord(httpRequest.body)) { throw new internalErrorClasses_js_1.BadRequestError('POST body missing, invalid Content-Type, or JSON object has no keys.'); } ensureQueryIsStringOrMissing(httpRequest.body.query); if (typeof httpRequest.body.variables === 'string') { throw new internalErrorClasses_js_1.BadRequestError('`variables` in a POST body should be provided as an object, not a recursively JSON-encoded string.'); } if (typeof httpRequest.body.extensions === 'string') { throw new internalErrorClasses_js_1.BadRequestError('`extensions` in a POST body should be provided as an object, not a recursively JSON-encoded string.'); } if ('extensions' in httpRequest.body && httpRequest.body.extensions !== null && !isStringRecord(httpRequest.body.extensions)) { throw new internalErrorClasses_js_1.BadRequestError('`extensions` in a POST body must be an object if provided.'); } if ('variables' in httpRequest.body && httpRequest.body.variables !== null && !isStringRecord(httpRequest.body.variables)) { throw new internalErrorClasses_js_1.BadRequestError('`variables` in a POST body must be an object if provided.'); } if ('operationName' in httpRequest.body && httpRequest.body.operationName !== null && typeof httpRequest.body.operationName !== 'string') { throw new internalErrorClasses_js_1.BadRequestError('`operationName` in a POST body must be a string if provided.'); } graphQLRequest = { query: fieldIfString(httpRequest.body, 'query'), operationName: fieldIfString(httpRequest.body, 'operationName'), variables: fieldIfRecord(httpRequest.body, 'variables'), extensions: fieldIfRecord(httpRequest.body, 'extensions'), http: httpRequest, }; break; } case 'GET': { const searchParams = new URLSearchParams(httpRequest.search); graphQLRequest = { query: searchParamIfSpecifiedOnce(searchParams, 'query'), operationName: searchParamIfSpecifiedOnce(searchParams, 'operationName'), variables: jsonParsedSearchParamIfSpecifiedOnce(searchParams, 'variables'), extensions: jsonParsedSearchParamIfSpecifiedOnce(searchParams, 'extensions'), http: httpRequest, }; break; } default: throw new internalErrorClasses_js_1.BadRequestError('Apollo Server supports only GET/POST requests.', { extensions: { http: { status: 405, headers: new HeaderMap_js_1.HeaderMap([['allow', 'GET, POST']]), }, }, }); } const graphQLResponse = await (0, ApolloServer_js_1.internalExecuteOperation)({ server, graphQLRequest, internals, schemaDerivedData, sharedResponseHTTPGraphQLHead, }, { contextValue }); if (graphQLResponse.body.kind === 'single') { if (!graphQLResponse.http.headers.get('content-type')) { const contentType = (0, ApolloServer_js_1.chooseContentTypeForSingleResultResponse)(httpRequest); if (contentType === null) { throw new internalErrorClasses_js_1.BadRequestError(`An 'accept' header was provided for this request which does not accept ` + `${ApolloServer_js_1.MEDIA_TYPES.APPLICATION_JSON} or ${ApolloServer_js_1.MEDIA_TYPES.APPLICATION_GRAPHQL_RESPONSE_JSON}`, { extensions: { http: { status: 406 } } }); } graphQLResponse.http.headers.set('content-type', contentType); } return { ...graphQLResponse.http, body: { kind: 'complete', string: await internals.stringifyResult(orderExecutionResultFields(graphQLResponse.body.singleResult)), }, }; } const acceptHeader = httpRequest.headers.get('accept'); if (!(acceptHeader && new negotiator_1.default({ headers: { accept: httpRequest.headers.get('accept') }, }).mediaType([ ApolloServer_js_1.MEDIA_TYPES.MULTIPART_MIXED_NO_DEFER_SPEC, ApolloServer_js_1.MEDIA_TYPES.MULTIPART_MIXED_EXPERIMENTAL, ]) === ApolloServer_js_1.MEDIA_TYPES.MULTIPART_MIXED_EXPERIMENTAL)) { throw new internalErrorClasses_js_1.BadRequestError('Apollo server received an operation that uses incremental delivery ' + '(@defer or @stream), but the client does not accept multipart/mixed ' + 'HTTP responses. To enable incremental delivery support, add the HTTP ' + "header 'Accept: multipart/mixed; deferSpec=20220824'.", { extensions: { http: { status: 406 } } }); } graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; deferSpec=20220824'); return { ...graphQLResponse.http, body: { kind: 'chunked', asyncIterator: writeMultipartBody(graphQLResponse.body.initialResult, graphQLResponse.body.subsequentResults), }, }; } exports.runHttpQuery = runHttpQuery; async function* writeMultipartBody(initialResult, subsequentResults) { yield `\r\n---\r\ncontent-type: application/json; charset=utf-8\r\n\r\n${JSON.stringify(orderInitialIncrementalExecutionResultFields(initialResult))}\r\n---${initialResult.hasNext ? '' : '--'}\r\n`; for await (const result of subsequentResults) { yield `content-type: application/json; charset=utf-8\r\n\r\n${JSON.stringify(orderSubsequentIncrementalExecutionResultFields(result))}\r\n---${result.hasNext ? '' : '--'}\r\n`; } } function orderExecutionResultFields(result) { return { errors: result.errors, data: result.data, extensions: result.extensions, }; } function orderInitialIncrementalExecutionResultFields(result) { return { hasNext: result.hasNext, errors: result.errors, data: result.data, incremental: orderIncrementalResultFields(result.incremental), extensions: result.extensions, }; } function orderSubsequentIncrementalExecutionResultFields(result) { return { hasNext: result.hasNext, incremental: orderIncrementalResultFields(result.incremental), extensions: result.extensions, }; } function orderIncrementalResultFields(incremental) { return incremental?.map((i) => ({ hasNext: i.hasNext, errors: i.errors, path: i.path, label: i.label, data: i.data, items: i.items, extensions: i.extensions, })); } function prettyJSONStringify(value) { return JSON.stringify(value) + '\n'; } exports.prettyJSONStringify = prettyJSONStringify; function newHTTPGraphQLHead(status) { return { status, headers: new HeaderMap_js_1.HeaderMap(), }; } exports.newHTTPGraphQLHead = newHTTPGraphQLHead; function mergeHTTPGraphQLHead(target, source) { if (source.status) { target.status = source.status; } if (source.headers) { for (const [name, value] of source.headers) { target.headers.set(name, value); } } } exports.mergeHTTPGraphQLHead = mergeHTTPGraphQLHead; //# sourceMappingURL=runHttpQuery.js.map