UNPKG

@skele/classic

Version:

Skele is an architectural framework that assists with building data-driven apps with React or React Native.

190 lines (167 loc) 3.83 kB
'use strict' import * as R from 'ramda' import { fromJS } from 'immutable' export function execute(url, options) { const defaults = { revalidate: false, method: 'GET', } let opts if (typeof options === 'boolean') { opts = { ...defaults, revalidate: options, } } else { opts = { ...defaults, ...options, } } const headers = new Headers() if (opts.revalidate) { headers.append('Cache-Control', 'max-age=0') } if (opts.headers) { R.forEachObjIndexed((v, h) => headers.append(h, v), opts.headers) } const fetchOptions = { ...R.omit(['revalidate', 'headers'], opts), headers } return fetch(url, fetchOptions) .then(processFetchResponse) .catch(errorResponseForUrl(url)) } export function post(url, json, options) { const opts = { ...options, ...jsonBody(json, options), method: 'POST', } return execute(url, opts) } export const get = execute export const httpRead = get export function options(uri, opts) { return execute(uri, { ...opts, method: 'OPTIONS', }) } export function del(uri, opts) { return execute(uri, { ...opts, method: 'DELETE', }) } export function head(uri, opts) { return execute(uri, { ...opts, method: 'HEAD', }) } export function put(uri, json, options) { const opts = { ...options, ...jsonBody(json, options), method: 'PUT', } return execute(uri, opts) } export function patch(uri, json, options) { const opts = { ...options, ...jsonBody(json, options), method: 'PATCH', } return execute(uri, opts) } function jsonBody(json, options) { return { body: JSON.stringify(json), headers: { ...R.defaultTo({}, R.prop('headers', options)), 'Content-Type': 'application/json', }, } } function processFetchResponse(resp) { if (resp.ok) { return resp .json() .then(json => ({ value: fromJS(json), meta: responseMeta(resp) })) .catch(errorResponseForUrl(resp.url)) } else { return { meta: responseMeta(resp) } } } function errorResponseForUrl(url) { return error => ({ meta: { url, uri: url, status: 998, message: error != null ? error.message : undefined, error, }, }) } export function asResponse(value, from = undefined) { return { value, ...(from != null ? { meta: { status: 200, uri: from, url: from, message: 'OK' } } : {}), } } export function failedResponse(message, object = undefined, from = undefined) { if (from == null) { from = object object = undefined } return { ...(object != null ? { value: object } : {}), meta: { ...(from != null ? { uri: from, url: from } : {}), status: 999, message, }, } } const unwrap = R.prop('value') const wrap = (resp, v) => (isResponse(v) ? v : { ...resp, value: v }) /* * Like data.flow() but for responses; will unwrap before calling fn; * fn can return just a value or a full blown response */ export function flow(resp, ...fns) { return R.reduce( (v, fn) => (isOK(v) ? wrap(v, fn(unwrap(v))) : R.reduced(v)), resp, fns ) } export function responseMeta(resp) { let message = resp.statusText if (!message) { message = resp.ok || isOK(resp) ? 'OK' : 'Failure' } const uri = resp.uri || resp.url return { url: uri, uri, status: resp.status ? resp.status : 200, message: message, } } export function isResponse(response) { return ( response != null && (response.value != null || R.path(['meta', 'status'], response) != null) ) } export function isOK(response) { const status = R.path(['meta', 'status'], response) return ( (response.value != null && status == null) || (response.value != null && status >= 200 && status < 300) ) }