UNPKG

graphql-yoga

Version:
94 lines (93 loc) 3.39 kB
import { createGraphQLError } from '@graphql-tools/utils'; import { handleMaybePromise } from '@whatwg-node/promise-helpers'; import { isAbortError } from '../../error.js'; import { isContentTypeMatch } from './utils.js'; export function isPOSTMultipartRequest(request) { return request.method === 'POST' && isContentTypeMatch(request, 'multipart/form-data'); } export function parsePOSTMultipartRequest(request) { return handleMaybePromise(() => request.formData(), (requestBody) => { const operationsStr = requestBody.get('operations'); if (!operationsStr) { throw createGraphQLError('Missing multipart form field "operations"'); } if (typeof operationsStr !== 'string') { throw createGraphQLError('Multipart form field "operations" must be a string'); } let operations; try { operations = JSON.parse(operationsStr); } catch { throw createGraphQLError('Multipart form field "operations" must be a valid JSON string'); } const mapStr = requestBody.get('map'); if (mapStr != null) { if (typeof mapStr !== 'string') { throw createGraphQLError('Multipart form field "map" must be a string'); } let map; try { map = JSON.parse(mapStr); } catch { throw createGraphQLError('Multipart form field "map" must be a valid JSON string'); } for (const fileIndex in map) { const file = requestBody.get(fileIndex); const keys = map[fileIndex]; for (const key of keys) { setObjectKeyPath(operations, key, file); } } } return operations; }, e => { if (isAbortError(e)) { throw e; } if (e instanceof Error) { if (e.message.startsWith('File size limit exceeded: ')) { throw createGraphQLError(e.message, { extensions: { http: { status: 413, }, code: 'REQUEST_ENTITY_TOO_LARGE', }, }); } throw createGraphQLError(`POST body sent invalid multipart data: ${e.message}`, { extensions: { http: { status: 400, }, code: 'BAD_REQUEST', }, }); } throw e; }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any function setObjectKeyPath(object, keyPath, value) { const keys = keyPath.split('.'); let current = object; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (key === '__proto__' || key === 'constructor' || key === 'prototype') { return; } const isLastKey = i === keys.length - 1; if (isLastKey) { current[key] = value; } else { const isObject = typeof current[key] === 'object' && current[key] !== null; if (!Object.hasOwn(current, key) || !isObject) { current[key] = {}; } current = current[key]; } } }