@aspectus/resource
Version:
Composable fetch API interface.
200 lines (169 loc) • 5.46 kB
JavaScript
/* eslint-disable prefer-spread */
import pipe from 'ramda/src/pipe';
import prop from 'ramda/src/prop';
import { createResource } from './resource';
import { sameResult, isFunction } from './utils';
const orderCmp = (a, b) => a.order - b.order || a.index - b.index;
const transformerTransducer = (acc, t) => acc.then.apply(acc, t);
class ResourceChain {
constructor(parameters) {
this.parameters = parameters || {};
}
getResource() {
if (!this.preparedResource) {
this.preparedResource = createResource(this.getParameters());
}
return this.preparedResource;
}
getParameters() {
if (!this.preparedParameters) {
this.preparedParameters = {
...this.parameters,
middleware: this.getMiddleware(),
};
}
return this.preparedParameters;
}
getMiddleware() {
if (this.preparedMiddleware) {
return this.preparedMiddleware;
}
const { middleware = [] } = this.parameters;
this.preparedMiddleware = middleware.length
? pipe.apply(
this, middleware.sort(orderCmp).map(prop('middleware'))
)
: sameResult;
return this.preparedMiddleware;
}
getTransformers() {
if (this.preparedTransformers) {
return this.preparedTransformers;
}
const { transformers = [] } = this.parameters;
this.preparedTransformers = transformers.sort(orderCmp).map(prop('args'));
return this.preparedTransformers;
}
/**
* Overrides `urlGetter`.
*
* @param {Function} urlGetter - Function that returns url based on passed
* parameters.
* @returns Resource - New Resource instance with overrided urlGetter.
* @memberof Resource
*/
url(urlGetter) {
return new this.constructor({ ...this.parameters, urlGetter });
}
/**
* Overrides `fetcher`.
*
* @param {Function} fetcher - Function that implements Fetch API.
* @returns Resource - New Resource instance with overrided fetcher.
* @memberof Resource
*/
fetcher(fetcher) {
return new this.constructor({ ...this.parameters, fetcher });
}
/**
* Adds new middleware to the set of existing middlewares list.
*
* @param {Function} middleware - Middleware that transforms parameters,
* config, body, headers to fit your needs.
* @param {int} order - Middleware execution order. Must be between 0 - 1000.
* Default is 500.
* @returns Resource - New Resource instance with added middleware.
* @memberof Resource
*/
middleware(middleware, order = 500) {
const previous = this.parameters.middleware || [];
return new this.constructor({
...this.parameters,
middleware: [{ middleware, order, index: previous.length }].concat(previous),
});
}
/**
* Adds new transformer to the set of existing transformers list.
*
* @param {Function} then - Transformer that transforms result body.
* @param {Function} catcher - Error catcher.
* @param {int} order - Transformer execution order. Must be between 0 - 1000.
* Default is 500.
* @returns Resource - New Resource instance with added transformer.
* @memberof Resource
*/
transform(then, catcher, order = 500) {
const args = [then];
const previous = this.parameters.transformers || [];
if (isFunction(catcher)) {
args.push(catcher);
}
return new this.constructor({
...this.parameters,
transformers: [{ args, order, index: previous.length }].concat(previous),
});
}
merge(config, key, value = null) {
const object = value !== null ? { [key]: value } : key;
return new this.constructor({
...this.parameters, [config]: { ...this.parameters[config], ...object },
});
}
/**
* Object to merge into `config`(Fetch API configuration object).
*
* @param {*} key - Key for a singular change. Or Object with multiple
* key-value pairs.
* @param {*} value - Value for provided key. Or nothing if the first
* argument is Object.
* @returns Resource - New Resource instance with changed Fetch API config.
* @memberof Resource
*/
config(key, value) {
return this.merge('config', key, value);
}
/**
* Object to merge into `headers` configuration.
*
* @param {*} key - Key for a singular change. Or Object with multiple
* headers.
* @param {string} value - Value for provided key. Or nothing if the first
* argument is Object.
* @returns Resource - New Resource instance with changed headers.
* @memberof Resource
*/
headers(key, value) {
return this.merge('headers', key, value);
}
/**
* Make API call to the resource
*
* @param {*} args - Arguments, required to make an API call.
* @returns Promise - Result of the api call.
* @memberof Resource
*/
makeRequest() {
const resource = this.getResource();
return resource.apply(this, arguments); // eslint-disable-line prefer-rest-params
}
applyTransformers(promise) {
const transformers = this.getTransformers();
return transformers.reduce(transformerTransducer, promise);
}
/**
* Execute chain
*
* @param {*} args - Arguments, required to make an API call.
* @returns Promise - Transformed result of api call.
* @memberof Resource
*/
execute() {
return this.applyTransformers(
this.makeRequest.apply(this, arguments) // eslint-disable-line prefer-rest-params
);
}
}
export {
ResourceChain,
};
export default ResourceChain;