UNPKG

telefunc

Version:

Remote functions. Instead of API.

99 lines (98 loc) 4.33 kB
export { parseHttpRequest }; import { parse } from '@brillout/json-serializer/parse'; import { assertUsage, hasProp, getProjectError, getUrlPathname, assert, getTelefunctionKey, isProduction, } from '../../utils.js'; function parseHttpRequest(runContext) { assertUrl(runContext); if (isWrongMethod(runContext)) { return { isMalformed: true }; } const { body } = runContext.httpRequest; if (typeof body !== 'string') { if (runContext.logInvalidRequests) { assertBody(body, runContext); } else { // In production `body` can be any value really. // Therefore we `assertBody(body)` only development. } return { isMalformed: true }; } const bodyString = body; let bodyParsed; try { bodyParsed = parse(bodyString); } catch (err) { logParseError([ 'The argument `body` passed to `telefunc({ body })`', 'could not be parsed', `(\`body === '${bodyString}'\`).`, !hasProp(err, 'message') ? null : `Parse error: ${err.message}.`, ] .filter(Boolean) .join(' '), runContext); return { isMalformed: true }; } if (!hasProp(bodyParsed, 'file', 'string') || !hasProp(bodyParsed, 'name', 'string') || !hasProp(bodyParsed, 'args', 'array')) { logParseError([ 'The argument `body` passed to `telefunc({ body })`', 'can be parsed but its content is invalid', `(\`body === '${bodyString}'\`).`, ].join(' '), runContext); return { isMalformed: true }; } const telefuncFilePath = bodyParsed.file; const telefunctionName = bodyParsed.name; const telefunctionArgs = bodyParsed.args; const telefunctionKey = getTelefunctionKey(telefuncFilePath, telefunctionName); return { telefuncFilePath, telefunctionName, telefunctionKey, telefunctionArgs, isMalformed: false, }; } function assertBody(body, runContext) { const errorNote = [ `Make sure that \`body\` is the HTTP body string of the request HTTP POST \`Content-Type: text/plain\` \`${runContext.serverConfig.telefuncUrl}\`.`, 'Note that with some server frameworks, such as Express.js, a server middleware is needed to process the HTTP body of `Content-Type: text/plain` requests.', ].join(' '); assertUsage(body !== undefined && body !== null, ['`telefunc({ body })`: argument `body` should be a string but', `\`body === ${body}\`.`, errorNote].join(' ')); assertUsage(typeof body === 'string', [ '`telefunc({ body })`: argument `body` should be a string but', `\`typeof body === '${typeof body}'\`.`, errorNote, ].join(' ')); assertUsage( // Express.js sets `req.body === '{}'` upon wrong Content-Type body !== '{}', ["`telefunc({ body })`: argument `body` is an empty JSON object (`body === '{}'`).", errorNote].join(' ')); } function isWrongMethod(runContext) { if (['POST', 'post'].includes(runContext.httpRequest.method)) { return false; } assert(typeof runContext.httpRequest.method === 'string'); logParseError([ 'The argument `method` passed to `telefunc({ method })`', 'should be `POST` (or `post`) but', `\`method === '${runContext.httpRequest.method}'\`.`, ].join(' '), runContext); return true; } function assertUrl(runContext) { const urlPathname = getUrlPathname(runContext.httpRequest.url); assertUsage(urlPathname === runContext.serverConfig.telefuncUrl, `telefunc({ url }): The pathname of \`url\` is \`${urlPathname}\` but it's expected to be \`${runContext.serverConfig.telefuncUrl}\`. Either make sure that \`url\` is the HTTP request URL, or set \`config.telefuncUrl\` to \`${urlPathname}\`.`); } function logParseError(errMsg, runContext) { const errMsgPrefix = 'Malformed request in development.'; const errMsgSuffix = 'This is unexpected since, in development, all requests are expected to originate from the Telefunc Client and should therefore be valid.'; if (!isProduction()) { errMsg = `${errMsgPrefix} ${errMsg} ${errMsgSuffix}`; } if (runContext.logInvalidRequests) { console.error(getProjectError(errMsg)); } }