Files
jackbeeby b412dfe2ca Initialisation
Added the packages and files for the backend server
2024-12-15 17:48:45 +11:00

244 lines
11 KiB
JavaScript

"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