NodeBB/NodeBB

View on GitHub
src/request.js

Summary

Maintainability
A
25 mins
Test Coverage
'use strict';

const { CookieJar } = require('tough-cookie');
const fetchCookie = require('fetch-cookie').default;

exports.jar = function () {
    return new CookieJar();
};

async function call(url, method, { body, timeout, jar, ...config } = {}) {
    let fetchImpl = fetch;
    if (jar) {
        fetchImpl = fetchCookie(fetch, jar);
    }

    const jsonTest = /application\/([a-z]+\+)?json/;
    const opts = {
        ...config,
        method,
        headers: {
            'content-type': 'application/json',
            ...config.headers,
        },
    };
    if (timeout > 0) {
        opts.signal = AbortSignal.timeout(timeout);
    }

    if (body && ['POST', 'PUT', 'PATCH', 'DEL', 'DELETE'].includes(method)) {
        if (opts.headers['content-type'] && jsonTest.test(opts.headers['content-type'])) {
            opts.body = JSON.stringify(body);
        } else {
            opts.body = body;
        }
    }

    const response = await fetchImpl(url, opts);

    const { headers } = response;
    const contentType = headers.get('content-type');
    const isJSON = contentType && jsonTest.test(contentType);
    let respBody = await response.text();
    if (isJSON && respBody) {
        try {
            respBody = JSON.parse(respBody);
        } catch (err) {
            throw new Error('invalid json in response body', url);
        }
    }

    return {
        body: respBody,
        response: {
            ok: response.ok,
            status: response.status,
            statusCode: response.status,
            statusText: response.statusText,
            headers: Object.fromEntries(response.headers.entries()),
        },
    };
}

/*
const { body, response } = await request.get('someurl?foo=1&baz=2')
*/
exports.get = async (url, config) => call(url, 'GET', config);

exports.head = async (url, config) => call(url, 'HEAD', config);
exports.del = async (url, config) => call(url, 'DELETE', config);
exports.delete = exports.del;
exports.options = async (url, config) => call(url, 'OPTIONS', config);

/*
const { body, response } = await request.post('someurl', { body: { foo: 1, baz: 2}})
*/
exports.post = async (url, config) => call(url, 'POST', config);
exports.put = async (url, config) => call(url, 'PUT', config);
exports.patch = async (url, config) => call(url, 'PATCH', config);