UNPKG

@talend/react-cmf

Version:

A framework built on top of best react libraries

397 lines (380 loc) 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.HTTPError = exports.HTTP = void 0; exports.encodePayload = encodePayload; exports.getDefaultConfig = getDefaultConfig; exports.handleBody = handleBody; exports.handleCSRFToken = handleCSRFToken; exports.handleDefaultHttpConfiguration = void 0; exports.handleError = handleError; exports.handleHttpResponse = handleHttpResponse; exports.httpDelete = httpDelete; exports.httpFetch = httpFetch; exports.httpGet = httpGet; exports.httpHead = httpHead; exports.httpPatch = httpPatch; exports.httpPost = httpPost; exports.httpPut = httpPut; exports.setDefaultConfig = setDefaultConfig; exports.setDefaultLanguage = setDefaultLanguage; exports.wrapFetch = wrapFetch; var _effects = require("redux-saga/effects"); var _httpInterceptors = _interopRequireDefault(require("../httpInterceptors")); var _constants = require("../middlewares/http/constants"); var _csrfHandling = require("../middlewares/http/csrfHandling"); var _lodash = require("lodash"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * Storage point for the doc setup using `setDefaultConfig` */ const HTTP = exports.HTTP = { defaultConfig: null }; /** * merge the CSRFToken handling rule from the module defaultConfig * into config argument * @param {Object} config * @returns {Function} */ function handleCSRFToken(config) { return (0, _csrfHandling.mergeCSRFToken)({ security: config.security })(config); } class HTTPError extends Error { constructor({ data, response }) { super(response.statusText); this.name = `HTTP ${response.status}`; this.data = data; this.response = response; } } /** * handleHttpResponse - handle the http body * * @param {Response} response A response object * @return {Promise} A promise that resolves with the result of parsing the body */ exports.HTTPError = HTTPError; function handleBody(response, { method } = {}) { if (response.status === _constants.HTTP_STATUS.NO_CONTENT || method === _constants.HTTP_METHODS.HEAD) { return Promise.resolve({ data: '', response }); } let methodBody = 'text'; const headers = (0, _lodash.get)(response, 'headers', new Headers()); const contentType = headers.get('Content-Type'); if (contentType && contentType.includes('application/json')) { methodBody = 'json'; } if (contentType && contentType.includes('application/zip')) { methodBody = 'blob'; } return response[methodBody]().then(data => ({ data, response })); } /** * handleHttpResponse - handle the http error * * @param {Response} response A response object * @return {Promise} A promise that reject with the result of parsing the body */ function handleError(response, request = {}) { // in case of network issue if (response instanceof Error) { return new HTTPError({ response, data: response }); } return handleBody(response, request).then(body => new HTTPError(body)); } /** * handleHttpResponse - handle the http response * * @param {Response} response A response object * @return {Promise} A promise that: * - resolves with the result of parsing the body * - reject the response */ function handleHttpResponse(response, request = {}) { if (!_constants.testHTTPCode.isSuccess(response.status)) { return Promise.reject(response); } return handleBody(response, request); } /** * encodePayload - encore the payload if necessary * * @param {object} headers request headers * @param {object} payload payload to send with the request * @return {string|FormData} The encoded payload. */ function encodePayload(headers, payload) { const type = headers['Content-Type']; if (payload instanceof FormData || typeof payload === 'string') { return payload; } else if (type && type.includes('json')) { return JSON.stringify(payload); } return payload; } /** * httpFetch - call the api fetch to request the url * * @param {string} url url to request * @param {object} config option that you want apply to the request * @param {string} method = HTTP_METHODS.GET method to apply * @param {object} payload payload to send with the request * @return {Promise} A Promise that resolves to a Response object. */ function httpFetch(url, config, method, payload) { const defaultHeaders = { Accept: 'application/json', 'Content-Type': 'application/json' }; /** * If the playload is an instance of FormData the body should be set to this object * and the Content-type header should be stripped since the browser * have to build a special headers with file boundary in if said FormData is used to upload file */ if (payload instanceof FormData) { delete defaultHeaders['Content-Type']; } const params = (0, _lodash.merge)({ credentials: 'include', headers: defaultHeaders, method }, { ...HTTP.defaultConfig, ...config }); return fetch(url, handleCSRFToken({ ...params, body: encodePayload(params.headers, payload) })).then(response => handleHttpResponse(response, params)).catch(response => handleError(response, params)); } /** * function - wrap the fetch request with the actions errors * * @param {string} url url to request * @param {object} config option that you want apply to the request * @param {string} method = HTTP_METHODS.GET method to apply * @param {object} payload payload to send with the request * @param {object} options options to deal with cmf automatically * @return {object} the response of the request */ function* wrapFetch(url, config, method = _constants.HTTP_METHODS.GET, payload, options) { const newConfig = yield (0, _effects.call)(_httpInterceptors.default.onRequest, { url, method, payload, ...config }); const answer = yield (0, _effects.call)(httpFetch, newConfig.url, newConfig, newConfig.method, newConfig.payload); yield (0, _effects.call)(_httpInterceptors.default.onResponse, answer); const silent = (0, _lodash.get)(options, 'silent'); if (silent !== true && answer instanceof Error) { yield (0, _effects.put)({ error: { // allow RFC-7807 compliance ...(0, _lodash.get)(answer, 'data', {}), // legacy properties message: (0, _lodash.get)(answer, 'data.message'), stack: { status: (0, _lodash.get)(answer, 'response.status') } }, url, config, method, payload, options, type: _constants.ACTION_TYPE_HTTP_ERRORS }); } return answer; } /** * function - fetch a url with POST method * * @param {string} url url to request * @param {object} payload payload to send with the request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.post, '/foo', {foo: 42}); */ function* httpPost(url, payload, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.POST, payload, options); } /** * function - fetch a url with PATCH method * * @param {string} url url to request * @param {object} payload payload to send with the request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.patch, '/foo', {foo: 42}); */ function* httpPatch(url, payload, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.PATCH, payload, options); } /** * function - fetch a url with PUT method * * @param {string} url url to request * @param {object} payload payload to send with the request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.put, '/foo', {foo: 42}); */ function* httpPut(url, payload, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.PUT, payload, options); } /** * function - fetch a url with DELETE method * * @param {string} url url to request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.delete, '/foo'); */ function* httpDelete(url, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.DELETE, undefined, options); } /** * function - fetch a url with GET method * * @param {string} url url to request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.get, '/foo'); */ function* httpGet(url, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.GET, undefined, options); } /** * function - fetch a url with GET method * * @param {string} url url to request * @param {object} config option that you want apply to the request * @param {object} options options to deal with cmf automatically * @example * import { sagas } from '@talend/react-cmf'; * import { call } from 'redux-saga/effects' * yield call(sagas.http.get, '/foo'); */ function* httpHead(url, config, options) { return yield* wrapFetch(url, config, _constants.HTTP_METHODS.HEAD, undefined, options); } /** * setDefaultHeader - define a default config to use with the saga http * this default config is stored in this module for the whole application * * @param {object} config key/value of header to apply * @example * import { setDefaultConfig } from '@talend/react-cmf/sagas/http'; * setDefaultConfig({headers: { * 'Accept-Language': preferredLanguage, * }}); */ function setDefaultConfig(config) { if (HTTP.defaultConfig) { throw new Error('ERROR: setDefaultConfig should not be called twice, if you wish to change the language use setDefaultLanguage api.'); } HTTP.defaultConfig = config; } /** * To change only the Accept-Language default headers * on the global http defaultConfig * @param {String} language */ function setDefaultLanguage(language) { if ((0, _lodash.get)(HTTP, 'defaultConfig.headers')) { HTTP.defaultConfig.headers['Accept-Language'] = language; } else { // eslint-disable-next-line no-console throw new Error('ERROR: you should call setDefaultConfig.'); } } const handleDefaultHttpConfiguration = exports.handleDefaultHttpConfiguration = (0, _lodash.curry)((defaultHttpConfig, httpConfig) => /** * Wall of explain * merge mutate your object see https://lodash.com/docs/4.17.10#merge little note at the * end of the documentation, so why ? don't know but its bad. * * so defaultHttpConfig was mutated inside the curried function and applied to * all other call providing httpConfig, leading to interesting bug like having one time * httpConfig override merged into defaultHttConfig. * a test with two sccessive call will detect this issue. */ (0, _lodash.merge)({}, defaultHttpConfig, httpConfig)); /** * getDefaultConfig - return the defaultConfig * * @return {object} the defaultConfig used by cmf */ function getDefaultConfig() { return HTTP.defaultConfig; } var _default = exports.default = { delete: httpDelete, get: httpGet, head: httpHead, post: httpPost, put: httpPut, patch: httpPatch, setDefaultConfig, setDefaultLanguage, getDefaultConfig, create(createConfig = {}) { const configEnhancer = handleDefaultHttpConfiguration(createConfig); return { delete: function* configuredDelete(url, config = {}, options = {}) { return yield (0, _effects.call)(httpDelete, url, configEnhancer(config), options); }, get: function* configuredGet(url, config = {}, options = {}) { return yield (0, _effects.call)(httpGet, url, configEnhancer(config), options); }, post: function* configuredPost(url, payload, config = {}, options = {}) { return yield (0, _effects.call)(httpPost, url, payload, configEnhancer(config), options); }, put: function* configuredPut(url, payload, config = {}, options = {}) { return yield (0, _effects.call)(httpPut, url, payload, configEnhancer(config), options); }, patch: function* configuredPatch(url, payload, config = {}, options = {}) { return yield (0, _effects.call)(httpPatch, url, payload, configEnhancer(config), options); }, head: function* configuredPatch(url, config = {}, options = {}) { return yield (0, _effects.call)(httpHead, url, configEnhancer(config), options); } }; } }; //# sourceMappingURL=http.js.map