UNPKG

@obelisk/client

Version:

Typescript client to interact with Obelisk on a higher level than the regular ReST API calls.

202 lines (201 loc) 8.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InternalUtils = void 0; const rxjs_1 = require("rxjs"); const ajax_1 = require("rxjs/ajax"); const operators_1 = require("rxjs/operators"); const logger_1 = require("./logger"); const interfaces_1 = require("../interfaces"); /** * @hidden */ let InternalUtils = /** @class */ (() => { class InternalUtils { static mapAjaxResponse2TPageResponse(resp, index) { let result = { data: resp.response }; let linkHeaderValue = resp.xhr.getResponseHeader("link"); if (linkHeaderValue !== null) { let link = { link: InternalUtils.parseLinkHeader(linkHeaderValue) }; result = Object.assign(Object.assign({}, result), link); } return result; } static token2header(client, cfg) { if (!cfg.headers) { cfg.headers = {}; } cfg.headers = Object.assign(Object.assign({}, cfg.headers), { 'Authorization': 'Bearer ' + (client.tokens.rpt ? client.tokens.rpt.getToken() : '<no rpt present>') }); return cfg; } static parseLinkHeader(linkHeaderValue) { let links = {}; linkHeaderValue.split(',').forEach(line => { let parts = line.split(';'); let uri = parts[0].trim().slice(1, -1); let amount = parts.length; if (amount > 1) { for (let i = 1; i < amount; i++) { let part = parts[i].split('='); let key = part[0]; let value = part[1]; if ('rel' === key.trim()) { links[value.trim().slice(1, -1)] = uri; } else { console.log(key.trim() + ' => ' + value.trim()); } } } }); return links; } /** * Divide url in 2 parts: before /full/path and ?querystring as 2 item array. * @param uri */ static part(uri) { const idx = uri.lastIndexOf('?'); let result; if (idx === -1) { result = [uri]; } else { result = [uri.slice(0, idx), uri.slice(idx)]; } // Logger.debug(result, 'PART'); return result; } /** * Normalize the uri to an abstract uri, using the client settings. * @param client * @param uri */ static norm(client, uri, apiVersion = 'v1') { const schemePrefix = client.options.host + '/api/' + apiVersion; const apiPrefix = '/api/' + apiVersion; // https://idlab-iot.tengu.io/api/v1/context if (uri.startsWith(schemePrefix)) { return uri; } // /api/v1/context else if (uri.startsWith(apiPrefix)) { return uri.replace(apiPrefix, schemePrefix); } // /context or context else { uri = uri.startsWith('/') ? uri : '/' + uri; return schemePrefix + uri; } } static isTPageCompatible(client, uri, apiVersion = 'v1') { const check = InternalUtils.regex.test(InternalUtils.norm(client, InternalUtils.part(uri)[0], apiVersion)); // Logger.debug(InternalUtils.part(uri)[0], 'REGEX'); // Logger.debug(check, 'REGEX'); return check; } static isStreamEndpointCompatible(client, uri, apiVersion = 'v1') { const u = InternalUtils.norm(client, InternalUtils.part(uri)[0], apiVersion); return u.endsWith('/sse'); } /** * Catches the first 40x error, on 401, will redirect once and try to get a new token (refreshing too), * then retry the caught observable or request. If that doesn't work, the error is thrown anyway. * @param client ObeliskClient to get token from * @param request Optional AjaxRequest to retry instead of caught Observable */ static catch40xAndRetry(client, request) { return rxjs_1.pipe(operators_1.catchError((resp, caught) => { // Logger.debug('E', 'Caught generic error, probably 40x'); // 401 UNAUTHORIZED if (resp.status && resp.status === 401) { let wwwAuth; let retryable; if (request) { wwwAuth = resp.xhr.getResponseHeader('www-authenticate'); retryable = (c) => ajax_1.ajax(InternalUtils.token2header(c, request)); } else { wwwAuth = resp.headers['www-authenticate']; retryable = () => caught; } return InternalUtils.authAndRetry(client, wwwAuth, retryable); } // 403 FORBIDDEN handling else if (resp.status && resp.status === 403) { client.events$.next({ type: interfaces_1.ClientEventType.OnForbidden }); return rxjs_1.throwError(resp); } // All other cases else { return rxjs_1.throwError(resp); } })); } /** * Catches a 504 client and retries the original observable. * <strong>Hint</strong> Use this to work around listening to SSE channels that don't emit data for a long time. */ static catch504AndRetry() { return rxjs_1.pipe(operators_1.catchError((resp, caught) => { if (resp.status && resp.status === 504) { logger_1.Logger.debug('Retrying on 504 as requested', 'Error 504'); return caught; } // All other cases else { return rxjs_1.throwError(resp); } })); } /** * Send an authed AjaxRequest and does RPT handling. * 1. Get rpt, get new one if needed * 2. Catches errors once, then throws if not succeeded authed request * @param client * @param request */ static authRequest(client, request) { return ajax_1.ajax(InternalUtils.token2header(client, request)) .pipe(this.catch40xAndRetry(client, request)); } static createUUID() { var s = []; var hexDigits = '0123456789abcdef'; for (var i = 0; i < 36; i++) { s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); } s[14] = '4'; let t = s[19]; s[19] = hexDigits.substr((t & 0x3) | 0x8, 1); s[8] = s[13] = s[18] = s[23] = '-'; var uuid = s.join(''); return uuid; } } InternalUtils.regex = new RegExp('(\/events|\/stats|\/anonymous-events|\/latest|\/\d{8}|\/\d{8}\/\d{1,2}|\/\d{8}\/\d{1,2}\/\d{1,2})'); /** * First authenticates and gets a new PAT token, then requests a new RPT token, then retries. */ InternalUtils.authAndRetry = (client, wwwAuth, retryAble) => { const args = {}; if (wwwAuth && wwwAuth.indexOf('UMA') !== -1) { const t = wwwAuth.substring('UMA'.length).split(',').forEach(part => { const sides = part.split('='); const key = sides[0].trim(); const value = JSON.parse(sides[1]); args[key] = value; }); } // Refresh token if needed logger_1.Logger.debug('Refreshing token', 'AUTH'); return client.refreshPatToken().pipe(operators_1.flatMap(ok => { let ticket = undefined; if (client.options.authMode === 'uma') { ticket = args.ticket; } return client.getNewRpt(ticket); }), operators_1.flatMap(ok => retryAble(client)), operators_1.catchError(err => rxjs_1.throwError(err))); }; return InternalUtils; })(); exports.InternalUtils = InternalUtils;