@uxland/fetch-client
Advanced tools
Comparing version 1.0.0-alpha.13 to 1.0.0-alpha.14
@@ -6,2 +6,16 @@ # Change Log | ||
# [1.0.0-alpha.14](https://github.com/uxland/uxland/compare/@uxland/fetch-client@1.0.0-alpha.13...@uxland/fetch-client@1.0.0-alpha.14) (2020-10-08) | ||
### Bug Fixes | ||
* **fetch-client:** update documentation ([df89d3f](https://github.com/uxland/uxland/commit/df89d3f005eafb11b8090899135ff182658bd325)) | ||
* new fetch client ([a260d77](https://github.com/uxland/uxland/commit/a260d7799105a0806b908584a39dc526acc36514)) | ||
* refactor fetch-client ([4d1a29d](https://github.com/uxland/uxland/commit/4d1a29d49d29d9125d07d039ebb8e8063c2b2f93)) | ||
* update fetch-client docs ([53fa4e0](https://github.com/uxland/uxland/commit/53fa4e0353548d78205fb7b53ec34d57eafa626d)) | ||
# [1.0.0-alpha.13](https://github.com/uxland/uxland/compare/@uxland/fetch-client@1.0.0-alpha.12...@uxland/fetch-client@1.0.0-alpha.13) (2020-10-06) | ||
@@ -8,0 +22,0 @@ |
@@ -1,2 +0,2 @@ | ||
// Fetch Client v1.0.0-alpha.12 | ||
// Fetch Client v1.0.0-alpha.13 | ||
// https://github.com/uxland/uxland/tree/master/packages/fetch-client#readme | ||
@@ -7,394 +7,39 @@ // (c) 2020-2020 UXLand | ||
(function (factory) { | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
factory(); | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
factory(); | ||
}((function () { 'use strict'; | ||
function _typeof(obj) { | ||
"@babel/helpers - typeof"; | ||
/* | ||
* MIT License | ||
* | ||
* Copyright (c) 2020 UXLand | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
* this software and associated documentation files (the "Software"), to deal in | ||
* the Software without restriction, including without limitation the rights to use, | ||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the | ||
* Software, and to permit persons to whom the Software is furnished to do so, | ||
* subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH | ||
* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
return _typeof(obj); | ||
} | ||
var tslib_1 = require("tslib"); | ||
/** @namespace FetchClient */ | ||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { | ||
try { | ||
var info = gen[key](arg); | ||
var value = info.value; | ||
} catch (error) { | ||
reject(error); | ||
return; | ||
} | ||
if (info.done) { | ||
resolve(value); | ||
} else { | ||
Promise.resolve(value).then(_next, _throw); | ||
} | ||
} | ||
tslib_1.__exportStar(require("./fetch-client"), exports); | ||
function _asyncToGenerator(fn) { | ||
return function () { | ||
var self = this, | ||
args = arguments; | ||
return new Promise(function (resolve, reject) { | ||
var gen = fn.apply(self, args); | ||
function _next(value) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); | ||
} | ||
function _throw(err) { | ||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); | ||
} | ||
_next(undefined); | ||
}); | ||
}; | ||
} | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.registerResponseHandler = exports.fetchClient = exports.ServiceBase = exports.handleBapiret = exports.withSapClientParam = exports.withQueryParams = exports.withBaseUrl = exports.removeHeader = exports.withHeaders = exports.configure = exports.handleErrors = exports.handleResponse = exports.INVALID_REQUEST_EVENT = exports.RESET_CREDENTIALS_EVENT = exports.INVALID_CREDENTIALS_EVENT = void 0; | ||
var event_aggregator_1 = require("@uxland/event-aggregator"); | ||
var HEADER_CONTENT_TYPE = 'Content-Type'; | ||
var CONTENT_TYPE_JSON = 'application/json'; | ||
var SAP_CLIENT_PARAM = 'sap-client'; | ||
var ABSOLUTE_URL_REGEX = /^([a-z][a-z0-9+\-.]*:)?\/\//i; | ||
var BAPIRET_ERROR_CODE = 'E'; | ||
exports.INVALID_CREDENTIALS_EVENT = 'UXL-CLIENT:INVALID_CREDENTIALS_EVENT'; | ||
exports.RESET_CREDENTIALS_EVENT = 'UXL-CLIENT:RESET_CREDENTIALS_EVENT'; | ||
exports.INVALID_REQUEST_EVENT = 'UXL-CLIENT:INVALID_REQUEST_EVENT'; | ||
var configuration = { | ||
headers: { | ||
'Content-Type': CONTENT_TYPE_JSON | ||
}, | ||
credentials: 'include', | ||
mode: 'cors' | ||
}; | ||
var baseUrl; | ||
var defaultQueryParams; | ||
var serializeValue = function serializeValue(value) { | ||
return value === null || typeof value === 'undefined' ? '' : encodeURIComponent(value); | ||
}; | ||
var toQueryParams = function toQueryParams(queryParams, prefix) { | ||
var str = []; | ||
for (var p in queryParams) { | ||
if (queryParams.hasOwnProperty(p)) { | ||
var k = prefix ? prefix + '[' + p + ']' : p, | ||
v = queryParams[p]; | ||
str.push(v !== null && _typeof(v) === 'object' ? serialize(v, k) : encodeURIComponent(k) + '=' + serializeValue(v)); | ||
} | ||
} | ||
return str.join('&'); | ||
}; | ||
var getRequestUrl = function getRequestUrl(baseUrl, url) { | ||
var queryParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
if (ABSOLUTE_URL_REGEX.test(url)) { | ||
return url; | ||
} | ||
var resultingUrl = (baseUrl || '') + url; | ||
var params = toQueryParams(Object.assign({}, defaultQueryParams, queryParams)); | ||
return params ? resultingUrl + '?' + params : resultingUrl; | ||
}; | ||
var mergeRequest = function mergeRequest(requestInit, config) { | ||
requestInit = requestInit || {}; | ||
config = config || configuration; | ||
var headers = Object.assign({}, config.headers, requestInit.headers || {}); | ||
return Object.assign({}, config, requestInit, { | ||
headers: headers | ||
}); | ||
}; | ||
var getCode = function getCode(r) { | ||
var code = r ? r.result || r.RESULT : null; | ||
if (code) { | ||
try { | ||
return parseInt(code); | ||
} catch (e) { | ||
return null; | ||
} | ||
} | ||
return null; | ||
}; | ||
var isUnauthorizedResponse = function isUnauthorizedResponse(r) { | ||
var code = getCode(r); | ||
return code && code == 401; | ||
}; | ||
var isInvalidRequest = function isInvalidRequest(r) { | ||
var code = getCode(r); | ||
return code && code >= 400; | ||
}; | ||
var isResetCredentialsResponse = function isResetCredentialsResponse(r) { | ||
var code = getCode(r); | ||
return code && code == 402; | ||
}; | ||
var isResponseContentTypeJSON = function isResponseContentTypeJSON(response) { | ||
var contentType = response.headers.get(HEADER_CONTENT_TYPE); | ||
return contentType && contentType.indexOf(CONTENT_TYPE_JSON) !== -1; | ||
}; | ||
var getError = function getError(r) { | ||
return { | ||
status: getCode(r), | ||
statusText: r.text || r.TEXT | ||
}; | ||
}; | ||
var responseHandlers = []; | ||
var defaultResponseHandler = function defaultResponseHandler(result) { | ||
if (isUnauthorizedResponse(result)) { | ||
event_aggregator_1.publish(exports.INVALID_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isResetCredentialsResponse(result)) { | ||
event_aggregator_1.publish(exports.RESET_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isInvalidRequest(result)) { | ||
event_aggregator_1.publish(exports.INVALID_REQUEST_EVENT, getError(result)); | ||
throw getError(result); | ||
} | ||
return result; | ||
}; | ||
exports.handleResponse = function (response) { | ||
return isResponseContentTypeJSON(response) ? response.json().then(function (r) { | ||
return responseHandlers.reduce(function (previousValue, currentValue) { | ||
return currentValue(previousValue); | ||
}, r); | ||
}) : response.text(); | ||
}; | ||
exports.handleErrors = /*#__PURE__*/function () { | ||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(response) { | ||
var microTask, error, result; | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
microTask = function microTask() { | ||
return new Promise(function (resolve) { | ||
return resolve(); | ||
}); | ||
}; | ||
if (response.ok) { | ||
_context.next = 26; | ||
break; | ||
} | ||
_context.prev = 2; | ||
_context.next = 5; | ||
return response.json(); | ||
case 5: | ||
result = _context.sent; | ||
error = Object.assign(new Error(), result, { | ||
status: response.status, | ||
statusText: response.statusText | ||
}); | ||
_context.next = 12; | ||
break; | ||
case 9: | ||
_context.prev = 9; | ||
_context.t0 = _context["catch"](2); | ||
error = Object.assign(new Error(), { | ||
status: response.status, | ||
statusText: response.statusText | ||
}); | ||
case 12: | ||
if (!(response.status === 401)) { | ||
_context.next = 16; | ||
break; | ||
} | ||
_context.next = 15; | ||
return microTask(); | ||
case 15: | ||
event_aggregator_1.publish(exports.INVALID_CREDENTIALS_EVENT, error); | ||
case 16: | ||
if (!(response.status === 402)) { | ||
_context.next = 22; | ||
break; | ||
} | ||
_context.next = 19; | ||
return microTask(); | ||
case 19: | ||
event_aggregator_1.publish(exports.RESET_CREDENTIALS_EVENT, error); | ||
_context.next = 25; | ||
break; | ||
case 22: | ||
_context.next = 24; | ||
return microTask(); | ||
case 24: | ||
event_aggregator_1.publish(exports.INVALID_REQUEST_EVENT, error); | ||
case 25: | ||
throw error; | ||
case 26: | ||
return _context.abrupt("return", response); | ||
case 27: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, null, [[2, 9]]); | ||
})); | ||
return function (_x) { | ||
return _ref.apply(this, arguments); | ||
}; | ||
}(); | ||
exports.configure = function (config) { | ||
Object.assign(configuration, config); | ||
}; | ||
exports.withHeaders = function (headers) { | ||
Object.assign(configuration.headers, headers); | ||
}; | ||
exports.removeHeader = function (header) { | ||
if (configuration.headers[header]) delete configuration.headers[header]; | ||
}; | ||
exports.withBaseUrl = function (url) { | ||
baseUrl = url; | ||
}; | ||
exports.withQueryParams = function (queryParams) { | ||
defaultQueryParams = queryParams; | ||
}; | ||
exports.withSapClientParam = function (sapClient) { | ||
var queryParams = {}; | ||
queryParams[SAP_CLIENT_PARAM] = sapClient; | ||
exports.withQueryParams(queryParams); | ||
}; | ||
exports.handleBapiret = function (bapiRet) { | ||
var arr = [].concat(bapiRet); | ||
var ret = arr[0]; | ||
if (ret && ret.TYPE == BAPIRET_ERROR_CODE) throw new Error(ret.MESSAGE); | ||
return ret ? ret.MESSAGE : ''; | ||
}; | ||
var FetchClient = /*#__PURE__*/function () { | ||
function FetchClient() { | ||
_classCallCheck(this, FetchClient); | ||
this.handleBapiRet = exports.handleBapiret; | ||
} | ||
_createClass(FetchClient, [{ | ||
key: "fetch", | ||
value: function (_fetch) { | ||
function fetch(_x2, _x3, _x4) { | ||
return _fetch.apply(this, arguments); | ||
} | ||
fetch.toString = function () { | ||
return _fetch.toString(); | ||
}; | ||
return fetch; | ||
}(function (input, requestInit, queryParams) { | ||
return fetch(getRequestUrl(baseUrl, input, queryParams), mergeRequest(requestInit, configuration)).then(function (r) { | ||
return exports.handleErrors(r); | ||
}).then(function (r) { | ||
return exports.handleResponse(r); | ||
}); | ||
}) | ||
}]); | ||
return FetchClient; | ||
}(); | ||
var ServiceBase = /*#__PURE__*/function () { | ||
function ServiceBase() { | ||
_classCallCheck(this, ServiceBase); | ||
} | ||
_createClass(ServiceBase, [{ | ||
key: "fetchClient", | ||
get: function get() { | ||
if (this._fetchClient == null) this._fetchClient = new FetchClient(); | ||
return this._fetchClient; | ||
} | ||
}]); | ||
return ServiceBase; | ||
}(); | ||
exports.ServiceBase = ServiceBase; | ||
exports.fetchClient = new FetchClient(); | ||
exports.registerResponseHandler = function (handler) { | ||
responseHandlers.push(handler); | ||
}; | ||
exports.registerResponseHandler(defaultResponseHandler); | ||
}))); |
@@ -1,1 +0,1 @@ | ||
!function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(e,t,r,n,o,s,i){try{var u=e[s](i),a=u.value}catch(e){return void r(e)}u.done?t(a):Promise.resolve(a).then(n,o)}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function o(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),e}Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerResponseHandler=exports.fetchClient=exports.ServiceBase=exports.handleBapiret=exports.withSapClientParam=exports.withQueryParams=exports.withBaseUrl=exports.removeHeader=exports.withHeaders=exports.configure=exports.handleErrors=exports.handleResponse=exports.INVALID_REQUEST_EVENT=exports.RESET_CREDENTIALS_EVENT=exports.INVALID_CREDENTIALS_EVENT=void 0;var s=require("@uxland/event-aggregator"),t="application/json",u=/^([a-z][a-z0-9+\-.]*:)?\/\//i;exports.INVALID_CREDENTIALS_EVENT="UXL-CLIENT:INVALID_CREDENTIALS_EVENT",exports.RESET_CREDENTIALS_EVENT="UXL-CLIENT:RESET_CREDENTIALS_EVENT",exports.INVALID_REQUEST_EVENT="UXL-CLIENT:INVALID_REQUEST_EVENT";function c(e,t){var r,n,o,s=[];for(r in e){e.hasOwnProperty(r)&&(n=t?t+"["+r+"]":r,s.push(null!==(o=e[r])&&"object"===i(o)?serialize(o,n):encodeURIComponent(n)+"="+(null==(o=o)?"":encodeURIComponent(o))))}return s.join("&")}function p(e){var t=e?e.result||e.RESULT:null;if(t)try{return parseInt(t)}catch(e){return null}return null}function f(e){return{status:p(e),statusText:e.text||e.TEXT}}var E,l,h={headers:{"Content-Type":t},credentials:"include",mode:"cors"},x=[];exports.handleResponse=function(e){return function(e){e=e.headers.get("Content-Type");return e&&!!~e.indexOf(t)}(e)?e.json().then(function(e){return x.reduce(function(e,t){return t(e)},e)}):e.text()},exports.handleErrors=function(){var u,t=(u=regeneratorRuntime.mark(function e(t){var r,n;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(r=function(){return new Promise(function(e){return e()})},t.ok){e.next=26;break}return e.prev=2,e.next=5,t.json();case 5:n=e.sent,n=Object.assign(Error(),n,{status:t.status,statusText:t.statusText}),e.next=12;break;case 9:e.prev=9,e.t0=e.catch(2),n=Object.assign(Error(),{status:t.status,statusText:t.statusText});case 12:if(401===t.status)return e.next=15,r();e.next=16;break;case 15:s.publish(exports.INVALID_CREDENTIALS_EVENT,n);case 16:if(402===t.status)return e.next=19,r();e.next=22;break;case 19:s.publish(exports.RESET_CREDENTIALS_EVENT,n),e.next=25;break;case 22:return e.next=24,r();case 24:s.publish(exports.INVALID_REQUEST_EVENT,n);case 25:throw n;case 26:return e.abrupt("return",t);case 27:case"end":return e.stop()}},e,null,[[2,9]])}),function(){var e=this,i=arguments;return new Promise(function(t,r){var n=u.apply(e,i);function o(e){a(n,t,r,o,s,"next",e)}function s(e){a(n,t,r,o,s,"throw",e)}o(void 0)})});return function(e){return t.apply(this,arguments)}}(),exports.configure=function(e){Object.assign(h,e)},exports.withHeaders=function(e){Object.assign(h.headers,e)},exports.removeHeader=function(e){h.headers[e]&&delete h.headers[e]},exports.withBaseUrl=function(e){E=e},exports.withQueryParams=function(e){l=e},exports.withSapClientParam=function(e){var t={};t["sap-client"]=e,exports.withQueryParams(t)},exports.handleBapiret=function(e){e=[].concat(e)[0];if(e&&"E"==e.TYPE)throw Error(e.MESSAGE);return e?e.MESSAGE:""};var T=function(){function e(){r(this,e),this.handleBapiRet=exports.handleBapiret}function t(e,t,r){return n.apply(this,arguments)}var n;return o(e,[{key:"fetch",value:(n=function(e,t,r){return fetch(function(e,t,r){r=2<arguments.length&&void 0!==r?r:{};if(u.test(t))return t;t=(e||"")+t,r=c(Object.assign({},l,r));return r?t+"?"+r:t}(E,e,r),(t=Object.assign({},(e=(e=h)||h).headers,(r=(r=t)||{}).headers||{}),Object.assign({},e,r,{headers:t}))).then(function(e){return exports.handleErrors(e)}).then(function(e){return exports.handleResponse(e)})},t.toString=function(){return""+n},t)}]),e}();exports.ServiceBase=function(){function e(){r(this,e)}return o(e,[{key:"fetchClient",get:function(){return null==this._fetchClient&&(this._fetchClient=new T),this._fetchClient}}]),e}(),exports.fetchClient=new T,exports.registerResponseHandler=function(e){x.push(e)},exports.registerResponseHandler(function(e){if(function(e){e=p(e);return e&&401==e}(e))throw s.publish(exports.INVALID_CREDENTIALS_EVENT,e),f(e);if(function(e){e=p(e);return e&&402==e}(e))throw s.publish(exports.RESET_CREDENTIALS_EVENT,e),f(e);if(function(e){e=p(e);return e&&400<=e}(e))throw s.publish(exports.INVALID_REQUEST_EVENT,f(e)),f(e);return e})}); | ||
!function(e){"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("tslib").__exportStar(require("./fetch-client"),exports)}); |
177
es/index.js
@@ -24,177 +24,2 @@ /* | ||
/** @namespace FetchClient */ | ||
import { publish } from '@uxland/event-aggregator'; | ||
const HEADER_CONTENT_TYPE = 'Content-Type'; | ||
const CONTENT_TYPE_JSON = 'application/json'; | ||
const SAP_CLIENT_PARAM = 'sap-client'; | ||
const ABSOLUTE_URL_REGEX = /^([a-z][a-z0-9+\-.]*:)?\/\//i; | ||
const BAPIRET_ERROR_CODE = 'E'; | ||
export const INVALID_CREDENTIALS_EVENT = 'UXL-CLIENT:INVALID_CREDENTIALS_EVENT'; | ||
export const RESET_CREDENTIALS_EVENT = 'UXL-CLIENT:RESET_CREDENTIALS_EVENT'; | ||
export const INVALID_REQUEST_EVENT = 'UXL-CLIENT:INVALID_REQUEST_EVENT'; | ||
const configuration = { | ||
headers: { | ||
'Content-Type': CONTENT_TYPE_JSON | ||
}, | ||
credentials: 'include', | ||
mode: 'cors' | ||
}; | ||
let baseUrl; | ||
let defaultQueryParams; | ||
const serializeValue = function (value) { | ||
return value === null || typeof value === 'undefined' ? '' : encodeURIComponent(value); | ||
}; | ||
const toQueryParams = function (queryParams, prefix) { | ||
const str = []; | ||
for (const p in queryParams) { | ||
if (queryParams.hasOwnProperty(p)) { | ||
const k = prefix ? prefix + '[' + p + ']' : p, v = queryParams[p]; | ||
str.push(v !== null && typeof v === 'object' ? serialize(v, k) : encodeURIComponent(k) + '=' + serializeValue(v)); | ||
} | ||
} | ||
return str.join('&'); | ||
}; | ||
const getRequestUrl = function (baseUrl, url, queryParams = {}) { | ||
if (ABSOLUTE_URL_REGEX.test(url)) { | ||
return url; | ||
} | ||
const resultingUrl = (baseUrl || '') + url; | ||
const params = toQueryParams(Object.assign({}, defaultQueryParams, queryParams)); | ||
return params ? resultingUrl + '?' + params : resultingUrl; | ||
}; | ||
const mergeRequest = function (requestInit, config) { | ||
requestInit = requestInit || {}; | ||
config = config || configuration; | ||
const headers = Object.assign({}, config.headers, requestInit.headers || {}); | ||
return Object.assign({}, config, requestInit, { headers: headers }); | ||
}; | ||
const getCode = (r) => { | ||
const code = r ? r.result || r.RESULT : null; | ||
if (code) { | ||
try { | ||
return parseInt(code); | ||
} | ||
catch (e) { | ||
return null; | ||
} | ||
} | ||
return null; | ||
}; | ||
const isUnauthorizedResponse = (r) => { | ||
const code = getCode(r); | ||
return code && code == 401; | ||
}; | ||
const isInvalidRequest = (r) => { | ||
const code = getCode(r); | ||
return code && code >= 400; | ||
}; | ||
const isResetCredentialsResponse = (r) => { | ||
const code = getCode(r); | ||
return code && code == 402; | ||
}; | ||
const isResponseContentTypeJSON = (response) => { | ||
const contentType = response.headers.get(HEADER_CONTENT_TYPE); | ||
return contentType && contentType.indexOf(CONTENT_TYPE_JSON) !== -1; | ||
}; | ||
const getError = (r) => { | ||
return { status: getCode(r), statusText: r.text || r.TEXT }; | ||
}; | ||
const responseHandlers = []; | ||
const defaultResponseHandler = (result) => { | ||
if (isUnauthorizedResponse(result)) { | ||
publish(INVALID_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isResetCredentialsResponse(result)) { | ||
publish(RESET_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isInvalidRequest(result)) { | ||
publish(INVALID_REQUEST_EVENT, getError(result)); | ||
throw getError(result); | ||
} | ||
return result; | ||
}; | ||
export const handleResponse = (response) => { | ||
return isResponseContentTypeJSON(response) | ||
? response | ||
.json() | ||
.then(r => responseHandlers.reduce((previousValue, currentValue) => currentValue(previousValue), r)) | ||
: response.text(); | ||
}; | ||
export const handleErrors = async (response) => { | ||
const microTask = () => new Promise(resolve => resolve()); | ||
if (!response.ok) { | ||
let error; | ||
try { | ||
const result = await response.json(); | ||
error = Object.assign(new Error(), result, { status: response.status, statusText: response.statusText }); | ||
} | ||
catch (e) { | ||
error = Object.assign(new Error(), { status: response.status, statusText: response.statusText }); | ||
} | ||
if (response.status === 401) { | ||
await microTask(); | ||
publish(INVALID_CREDENTIALS_EVENT, error); | ||
} | ||
if (response.status === 402) { | ||
await microTask(); | ||
publish(RESET_CREDENTIALS_EVENT, error); | ||
} | ||
else { | ||
await microTask(); | ||
publish(INVALID_REQUEST_EVENT, error); | ||
} | ||
throw error; | ||
} | ||
return response; | ||
}; | ||
export const configure = (config) => { | ||
Object.assign(configuration, config); | ||
}; | ||
export const withHeaders = (headers) => { | ||
Object.assign(configuration.headers, headers); | ||
}; | ||
export const removeHeader = (header) => { | ||
if (configuration.headers[header]) | ||
delete configuration.headers[header]; | ||
}; | ||
export const withBaseUrl = (url) => { | ||
baseUrl = url; | ||
}; | ||
export const withQueryParams = (queryParams) => { | ||
defaultQueryParams = queryParams; | ||
}; | ||
export const withSapClientParam = (sapClient) => { | ||
const queryParams = {}; | ||
queryParams[SAP_CLIENT_PARAM] = sapClient; | ||
withQueryParams(queryParams); | ||
}; | ||
export const handleBapiret = (bapiRet) => { | ||
const arr = [].concat(bapiRet); | ||
const ret = arr[0]; | ||
if (ret && ret.TYPE == BAPIRET_ERROR_CODE) | ||
throw new Error(ret.MESSAGE); | ||
return ret ? ret.MESSAGE : ''; | ||
}; | ||
class FetchClient { | ||
constructor() { | ||
this.handleBapiRet = handleBapiret; | ||
} | ||
fetch(input, requestInit, queryParams) { | ||
return fetch(getRequestUrl(baseUrl, input, queryParams), mergeRequest(requestInit, configuration)) | ||
.then(r => handleErrors(r)) | ||
.then(r => handleResponse(r)); | ||
} | ||
} | ||
export class ServiceBase { | ||
get fetchClient() { | ||
if (this._fetchClient == null) | ||
this._fetchClient = new FetchClient(); | ||
return this._fetchClient; | ||
} | ||
} | ||
export const fetchClient = new FetchClient(); | ||
export const registerResponseHandler = (handler) => { | ||
responseHandlers.push(handler); | ||
}; | ||
registerResponseHandler(defaultResponseHandler); | ||
export * from "./fetch-client"; |
/** @namespace FetchClient */ | ||
export interface Configuration { | ||
headers?: any; | ||
credentials?: string; | ||
mode: string; | ||
} | ||
export interface BapiRet { | ||
TYPE: string; | ||
MESSAGE: any; | ||
LOG_NO: any; | ||
} | ||
export interface InvalidRequestPayload { | ||
statusText?: string; | ||
status?: number | void; | ||
} | ||
export interface IFetchClient { | ||
fetch(input: any, requestInit?: any, queryParams?: any): Promise<any>; | ||
handleBapiRet(bapiRet: BapiRet): any; | ||
} | ||
export declare type ResponseHandler = <T>(response: any) => T; | ||
export declare const INVALID_CREDENTIALS_EVENT = "UXL-CLIENT:INVALID_CREDENTIALS_EVENT"; | ||
export declare const RESET_CREDENTIALS_EVENT = "UXL-CLIENT:RESET_CREDENTIALS_EVENT"; | ||
export declare const INVALID_REQUEST_EVENT = "UXL-CLIENT:INVALID_REQUEST_EVENT"; | ||
export declare const handleResponse: <T>(response: Response) => Promise<T>; | ||
export declare const handleErrors: (response: Response) => Promise<Response | never>; | ||
export declare const configure: (config: Configuration) => void; | ||
export declare const withHeaders: (headers: any) => void; | ||
export declare const removeHeader: (header: string) => void; | ||
export declare const withBaseUrl: (url: string) => void; | ||
export declare const withQueryParams: (queryParams: any) => void; | ||
export declare const withSapClientParam: (sapClient: string) => void; | ||
export declare const handleBapiret: (bapiRet: BapiRet | BapiRet[]) => string; | ||
declare class FetchClient implements IFetchClient { | ||
fetch<T>(input: any, requestInit?: any, queryParams?: any): Promise<T>; | ||
handleBapiRet: (bapiRet: BapiRet | BapiRet[]) => string; | ||
} | ||
export declare class ServiceBase { | ||
private _fetchClient; | ||
get fetchClient(): IFetchClient; | ||
} | ||
export declare const fetchClient: FetchClient; | ||
export declare const registerResponseHandler: (handler: ResponseHandler) => void; | ||
export {}; | ||
export * from "./fetch-client"; |
182
lib/index.js
@@ -24,181 +24,5 @@ "use strict"; | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
/** @namespace FetchClient */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.registerResponseHandler = exports.fetchClient = exports.ServiceBase = exports.handleBapiret = exports.withSapClientParam = exports.withQueryParams = exports.withBaseUrl = exports.removeHeader = exports.withHeaders = exports.configure = exports.handleErrors = exports.handleResponse = exports.INVALID_REQUEST_EVENT = exports.RESET_CREDENTIALS_EVENT = exports.INVALID_CREDENTIALS_EVENT = void 0; | ||
const event_aggregator_1 = require("@uxland/event-aggregator"); | ||
const HEADER_CONTENT_TYPE = 'Content-Type'; | ||
const CONTENT_TYPE_JSON = 'application/json'; | ||
const SAP_CLIENT_PARAM = 'sap-client'; | ||
const ABSOLUTE_URL_REGEX = /^([a-z][a-z0-9+\-.]*:)?\/\//i; | ||
const BAPIRET_ERROR_CODE = 'E'; | ||
exports.INVALID_CREDENTIALS_EVENT = 'UXL-CLIENT:INVALID_CREDENTIALS_EVENT'; | ||
exports.RESET_CREDENTIALS_EVENT = 'UXL-CLIENT:RESET_CREDENTIALS_EVENT'; | ||
exports.INVALID_REQUEST_EVENT = 'UXL-CLIENT:INVALID_REQUEST_EVENT'; | ||
const configuration = { | ||
headers: { | ||
'Content-Type': CONTENT_TYPE_JSON | ||
}, | ||
credentials: 'include', | ||
mode: 'cors' | ||
}; | ||
let baseUrl; | ||
let defaultQueryParams; | ||
const serializeValue = function (value) { | ||
return value === null || typeof value === 'undefined' ? '' : encodeURIComponent(value); | ||
}; | ||
const toQueryParams = function (queryParams, prefix) { | ||
const str = []; | ||
for (const p in queryParams) { | ||
if (queryParams.hasOwnProperty(p)) { | ||
const k = prefix ? prefix + '[' + p + ']' : p, v = queryParams[p]; | ||
str.push(v !== null && typeof v === 'object' ? serialize(v, k) : encodeURIComponent(k) + '=' + serializeValue(v)); | ||
} | ||
} | ||
return str.join('&'); | ||
}; | ||
const getRequestUrl = function (baseUrl, url, queryParams = {}) { | ||
if (ABSOLUTE_URL_REGEX.test(url)) { | ||
return url; | ||
} | ||
const resultingUrl = (baseUrl || '') + url; | ||
const params = toQueryParams(Object.assign({}, defaultQueryParams, queryParams)); | ||
return params ? resultingUrl + '?' + params : resultingUrl; | ||
}; | ||
const mergeRequest = function (requestInit, config) { | ||
requestInit = requestInit || {}; | ||
config = config || configuration; | ||
const headers = Object.assign({}, config.headers, requestInit.headers || {}); | ||
return Object.assign({}, config, requestInit, { headers: headers }); | ||
}; | ||
const getCode = (r) => { | ||
const code = r ? r.result || r.RESULT : null; | ||
if (code) { | ||
try { | ||
return parseInt(code); | ||
} | ||
catch (e) { | ||
return null; | ||
} | ||
} | ||
return null; | ||
}; | ||
const isUnauthorizedResponse = (r) => { | ||
const code = getCode(r); | ||
return code && code == 401; | ||
}; | ||
const isInvalidRequest = (r) => { | ||
const code = getCode(r); | ||
return code && code >= 400; | ||
}; | ||
const isResetCredentialsResponse = (r) => { | ||
const code = getCode(r); | ||
return code && code == 402; | ||
}; | ||
const isResponseContentTypeJSON = (response) => { | ||
const contentType = response.headers.get(HEADER_CONTENT_TYPE); | ||
return contentType && contentType.indexOf(CONTENT_TYPE_JSON) !== -1; | ||
}; | ||
const getError = (r) => { | ||
return { status: getCode(r), statusText: r.text || r.TEXT }; | ||
}; | ||
const responseHandlers = []; | ||
const defaultResponseHandler = (result) => { | ||
if (isUnauthorizedResponse(result)) { | ||
event_aggregator_1.publish(exports.INVALID_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isResetCredentialsResponse(result)) { | ||
event_aggregator_1.publish(exports.RESET_CREDENTIALS_EVENT, result); | ||
throw getError(result); | ||
} | ||
if (isInvalidRequest(result)) { | ||
event_aggregator_1.publish(exports.INVALID_REQUEST_EVENT, getError(result)); | ||
throw getError(result); | ||
} | ||
return result; | ||
}; | ||
exports.handleResponse = (response) => { | ||
return isResponseContentTypeJSON(response) | ||
? response | ||
.json() | ||
.then(r => responseHandlers.reduce((previousValue, currentValue) => currentValue(previousValue), r)) | ||
: response.text(); | ||
}; | ||
exports.handleErrors = async (response) => { | ||
const microTask = () => new Promise(resolve => resolve()); | ||
if (!response.ok) { | ||
let error; | ||
try { | ||
const result = await response.json(); | ||
error = Object.assign(new Error(), result, { status: response.status, statusText: response.statusText }); | ||
} | ||
catch (e) { | ||
error = Object.assign(new Error(), { status: response.status, statusText: response.statusText }); | ||
} | ||
if (response.status === 401) { | ||
await microTask(); | ||
event_aggregator_1.publish(exports.INVALID_CREDENTIALS_EVENT, error); | ||
} | ||
if (response.status === 402) { | ||
await microTask(); | ||
event_aggregator_1.publish(exports.RESET_CREDENTIALS_EVENT, error); | ||
} | ||
else { | ||
await microTask(); | ||
event_aggregator_1.publish(exports.INVALID_REQUEST_EVENT, error); | ||
} | ||
throw error; | ||
} | ||
return response; | ||
}; | ||
exports.configure = (config) => { | ||
Object.assign(configuration, config); | ||
}; | ||
exports.withHeaders = (headers) => { | ||
Object.assign(configuration.headers, headers); | ||
}; | ||
exports.removeHeader = (header) => { | ||
if (configuration.headers[header]) | ||
delete configuration.headers[header]; | ||
}; | ||
exports.withBaseUrl = (url) => { | ||
baseUrl = url; | ||
}; | ||
exports.withQueryParams = (queryParams) => { | ||
defaultQueryParams = queryParams; | ||
}; | ||
exports.withSapClientParam = (sapClient) => { | ||
const queryParams = {}; | ||
queryParams[SAP_CLIENT_PARAM] = sapClient; | ||
exports.withQueryParams(queryParams); | ||
}; | ||
exports.handleBapiret = (bapiRet) => { | ||
const arr = [].concat(bapiRet); | ||
const ret = arr[0]; | ||
if (ret && ret.TYPE == BAPIRET_ERROR_CODE) | ||
throw new Error(ret.MESSAGE); | ||
return ret ? ret.MESSAGE : ''; | ||
}; | ||
class FetchClient { | ||
constructor() { | ||
this.handleBapiRet = exports.handleBapiret; | ||
} | ||
fetch(input, requestInit, queryParams) { | ||
return fetch(getRequestUrl(baseUrl, input, queryParams), mergeRequest(requestInit, configuration)) | ||
.then(r => exports.handleErrors(r)) | ||
.then(r => exports.handleResponse(r)); | ||
} | ||
} | ||
class ServiceBase { | ||
get fetchClient() { | ||
if (this._fetchClient == null) | ||
this._fetchClient = new FetchClient(); | ||
return this._fetchClient; | ||
} | ||
} | ||
exports.ServiceBase = ServiceBase; | ||
exports.fetchClient = new FetchClient(); | ||
exports.registerResponseHandler = (handler) => { | ||
responseHandlers.push(handler); | ||
}; | ||
exports.registerResponseHandler(defaultResponseHandler); | ||
tslib_1.__exportStar(require("./fetch-client"), exports); |
{ | ||
"name": "@uxland/fetch-client", | ||
"version": "1.0.0-alpha.13", | ||
"version": "1.0.0-alpha.14", | ||
"description": "Fetch Client", | ||
@@ -73,5 +73,6 @@ "author": "UXLand <dev@uxland.es>", | ||
"dependencies": { | ||
"@uxland/event-aggregator": "^1.0.0-alpha.13" | ||
"@uxland/event-aggregator": "^1.0.0-alpha.13", | ||
"ramda": "^0.27.1" | ||
}, | ||
"gitHead": "63004281582b00a7e9c3d0468537646158286c10" | ||
"gitHead": "4665f8cf6b8a27bc6d233aecfda518a51e593b49" | ||
} |
@@ -5,3 +5,3 @@ # UXL Fetch Client [](https://badge.fury.io/js/%40uxland%2Ffetch-client) | ||
| ----------------------------------------------- | --------------------------------------------- | ----------------------------------------- | ------------------------------------------- | ----------------------------------- | | ||
|  |  |  |  |  | | ||
|  |  |  |  |  | | ||
@@ -13,1 +13,68 @@ ## Installation | ||
## Usage | ||
### Configuration | ||
#### _Set Base URL_ | ||
All relative fetch will use provided url as its own base URL. If using absolute uri when fetching, this will not be taken into account. | ||
```typescript | ||
import { setBaseUrl } from '@uxland/fetch-client'; | ||
setBaseUrl('http://localhost'); | ||
``` | ||
#### _Register response handlers_ | ||
Register a response handler to manipulate result. | ||
```typescript | ||
import { registerResponseHandler } from '@uxland/fetch-client'; | ||
registerResponseHandler((response) => JSON.stringify(response)); | ||
``` | ||
#### _Change fetch client configuration_ | ||
Change configuration globally. All fetch requests will use this configuration. | ||
```typescript | ||
import { configure } from '@uxland/fetch-client'; | ||
configure({mode: 'no-cors',{ headers: {authorization: 'Bearer <token>' }}}); | ||
``` | ||
#### _Change fetch client headers_ | ||
Change headers globally. All fetch requests will use specified headers. | ||
```typescript | ||
import { setHeaders } from '@uxland/fetch-client'; | ||
setHeaders({ authorization: 'Bearer <token>' }); | ||
``` | ||
#### _Delete specific header_ | ||
Remove a header from global headers using provided id. | ||
```typescript | ||
import { removeHeader } from '@uxland/fetch-client'; | ||
removeHeader('authorization'); | ||
``` | ||
#### _Reset fetch client headers_ | ||
Reset global headers to default. | ||
```typescript | ||
import { resetHeaders } from '@uxland/fetch-client'; | ||
resetHeaders(); | ||
``` | ||
### Fetch data | ||
```typescript | ||
import { doFetch } from '@uxland/fetch-client'; | ||
const response = doFetch('/dummy', { mode: 'no-cors' }, { foo: 'bar' }) | ||
.then((r) => r) | ||
.catch((e) => e); | ||
``` | ||
In addition, fetchClient also publishes (via `@uxland/event-aggregator`) two events INVALID_CREDENTIALS_EVENT and INVALID_REQUEST_EVENT when credentials are invalid or request has failed. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
32
856
1
79
3
39278
2
+ Addedramda@^0.27.1