UNPKG

@r/api-client

Version:
210 lines (167 loc) 5.49 kB
import superagent from 'superagent'; import Events from './Events'; import { APIResponse } from './APIResponse'; import NoModelError from './errors/NoModelError'; import ResponseError from './errors/ResponseError'; import ValidationError from './errors/ValidationError'; const EventEmitterShim = { emit: () => {}, on: () => {}, off: () => {}, }; const DefaultOptions = { userAgent: 'snoodev3', origin: 'https://www.reddit.com', appName: 'node-api-client-v3', env: 'develop', token: '', timeout: 5000, eventEmitter: EventEmitterShim, }; export const makeOptions = (overrides={}) => { return { ...DefaultOptions, ...overrides, }; }; const getEmitter = (apiOptions) => { return (apiOptions.eventEmitter || EventEmitterShim); }; const requestAuthHeader = (apiOptions) => { const token = apiOptions.token; if (!token) { return {}; } return { Authorization: `Bearer ${token}` }; }; const requestHeaders = (apiOptions) => { const authHeaders = requestAuthHeader(apiOptions); return { ...(apiOptions.headers || {}), ...authHeaders, }; }; const requestPath = (apiOptions, path) => { let slash = '/'; if (path.indexOf('/') === 0) { slash = ''; } return `${apiOptions.origin}${slash}${path}`; }; const appParameter = (apiOptions) => { return `${apiOptions.appName}-${apiOptions.env}`; }; // DEPRECATED: use apiRequest instead export const rawSend = (apiOptions, method, path, data, type, cb) => { const origin = apiOptions.origin; const url = requestPath(apiOptions, path); const fakeReq = { origin, path, url, method, query: { ...data}, }; getEmitter(apiOptions).emit(Events.request, fakeReq); let s = superagent[method](url); s.set(requestHeaders(apiOptions)); if (type === 'query') { data.app = appParameter(apiOptions); s.query({ ...(apiOptions.queryParams || {}), ...data, app: appParameter(apiOptions), }); if (s.redirects) { s.redirects(0); } } else { s.query({ app: appParameter(apiOptions) }); s.type(type); s.send(data); } s.end((err, res) => { // handle super agent inconsistencies const req = res ? res.request : fakeReq; cb(err, res, req); }); }; export const validateData = (data, method, apiName, validator) => { if (!(data && validator)) { throw new ValidationError(apiName, undefined); } if (!validator(data)) { throw new ValidationError(apiName, data); } }; // DEPRECATED: use apiRequest instead export const runJson = (apiOptions, method, path, data, parseBody, parseMeta) => { if (!(apiOptions && method && path && data)) { throw new NoModelError(); } return new Promise((resolve, reject) => { rawSend(apiOptions, method, path, data, 'json', (err, res, req) => { handle(apiOptions, resolve, reject, err, res, req, method, path, data, parseBody, parseMeta); }); }); } // DEPRECATED: use apiRequest instead export const runForm = (apiOptions, method, path, data, parseBody, parseMeta) => { if (!(apiOptions && method && path && data)) { throw new NoModelError(); } return new Promise((resolve, reject) => { rawSend(apiOptions, method, path, data, 'form', (err, res, req) => { handle(apiOptions, resolve, reject, err, res, req, method, path, data, parseBody, parseMeta); }); }); }; // DEPRECATED: use apiRequest instead export const runQuery = (apiOptions, method, path, query, rawQuery, parseBody, parseMeta) => { if (!(apiOptions && method && path && query && rawQuery)) { throw new NoModelError(); } if (method === 'get') { query.raw_json = 1; } return new Promise((resolve, reject) => { rawSend(apiOptions, method, path, query, 'query', (err, res, req) => { handle(apiOptions, resolve, reject, err, res, req, method, path, rawQuery, parseBody, parseMeta); }); }); }; const normalizeRequest = (res, req) => { if (res && !req) { return res.request || res.req; } return req; }; const handle = (apiOptions, resolve, reject, err, res, req, method, path, query, parseBody, parseMeta) => { req = normalizeRequest(res, req); if (handleRequestIfFailed(apiOptions, err, res, req, method, path, reject)) { return; } getEmitter(apiOptions).emit(Events.response, req, res); const apiResponse = tryParseResponse(reject, res, req, method, path, query, parseBody, parseMeta); getEmitter(apiOptions).emit(Events.result, apiResponse); resolve(apiResponse); }; const handleRequestIfFailed = (apiOptions, err, res, req, method, path, reject) => { if ((!err && !res) || (res && res.ok)) { return; } if (err) { getEmitter(apiOptions).emit(Events.error, err, req); if (err && err.timeout) { err.status = 504; } return reject(new ResponseError(err, path)); } // otherwise there's res and res.ok === false return reject(new ResponseError(res, path)); }; const tryParseResponse = (reject, res, req, method, path, query, parseBody, parseMeta) => { try { return makeApiResponse(res, req, method, query, parseBody, parseMeta); } catch (e) { console.trace(e); reject(new ResponseError(e, path)); } }; const makeApiResponse = (res, req, method, query, parseBody, parseMeta) => { if (!parseBody) { return res.body; } const meta = parseMeta ? parseMeta(res, req, method) : res.headers; const apiResponse = new APIResponse(res, meta, query); parseBody(res, apiResponse, req, method); return apiResponse; };