UNPKG

groupby-api

Version:

Client for the GroupBy Searchandiser API.

180 lines 6.71 kB
import * as clone from 'clone'; import * as fetchPonyfill from 'fetch-ponyfill'; import * as qs from 'qs'; import { Normalizers } from '../utils'; import { Query } from './query'; const SEARCH = '/search'; const REFINEMENTS = '/refinements'; const INVALID_QUERY_ERROR = 'query was not of a recognised type'; const createTimeoutPromise = (timeout) => new Promise((resolve, reject) => { setTimeout(() => { reject(new BridgeTimeout(`Timed out in ${timeout} ms`)); }, timeout); }); export class BridgeTimeout extends Error { /* istanbul ignore next */ constructor(err) { super(err); } } export class BridgeError extends Error { /* istanbul ignore next */ constructor(statusText, status, data) { super(statusText); this.status = status; this.data = data; Object.setPrototypeOf(this, BridgeError.prototype); } get statusText() { return this.message; } } export const DEFAULT_CONFIG = { timeout: 1500 }; export class AbstractBridge { constructor(config) { this.fetch = fetchPonyfill().fetch; this.headers = {}; this.config = Object.assign({}, DEFAULT_CONFIG, config); } search(query, callback) { let { request, queryParams } = this.extractRequest(query); if (request === null) return this.generateError(INVALID_QUERY_ERROR, callback); const response = this.fireRequest(this.bridgeUrl, request, queryParams) .then(AbstractBridge.transformRecords) .then(AbstractBridge.transformRefinements); return this.handleResponse(response, callback); } refinements(query, navigationName, callback) { let { request } = this.extractRequest(query); if (request === null) return this.generateError(INVALID_QUERY_ERROR, callback); const refinementsRequest = { originalQuery: request, navigationName }; const response = this.fireRequest(this.refinementsUrl, refinementsRequest); return this.handleResponse(response, callback); } handleResponse(response, callback) { if (callback) { response.then((res) => callback(undefined, res), (err) => callback(err)); } else { return response; } } extractRequest(query) { switch (typeof query) { case 'string': return { request: new Query(query).build(), queryParams: {} }; case 'object': return query instanceof Query ? { request: query.build(), queryParams: query.queryParams } : { request: clone(query), queryParams: {} }; default: return { request: null, queryParams: null }; } } generateError(error, callback) { const err = new Error(error); if (callback) { callback(err); } else { return Promise.reject(err); } } // tslint:disable-next-line max-line-length fireRequest(url, body, queryParams = {}) { const options = { method: 'POST', headers: Object.assign({ 'Content-Type': 'application/json' }, this.headers), body: JSON.stringify(AbstractBridge.normalizeRequest(this.augmentRequest(body))), }; const params = qs.stringify(queryParams); url = params ? `${url}?${params}` : url; return Promise.race([this.fetch(url, options), createTimeoutPromise(this.config.timeout)]) .then((res) => { if (res.ok) { return res.json(); } else { return res.json().then((err) => { throw new BridgeError(res.statusText, res.status, err); }); } }) .catch((err) => { if (this.errorHandler) { this.errorHandler(err); } throw err; }); } static normalizeRequest(request) { Object.keys(Normalizers).forEach((normalize) => Normalizers[normalize](request)); return request; } static transform(response, key, callback) { if (response[key]) { return Object.assign(response, { [key]: response[key].map(callback) }); } else { return response; } } static transformRecords(response) { return AbstractBridge.transform(response, 'records', AbstractBridge.convertRecordFields); } static transformRefinements(response) { const transformed = AbstractBridge.transform(response, 'availableNavigation', AbstractBridge.convertRefinement); return AbstractBridge.transform(transformed, 'selectedNavigation', AbstractBridge.convertRefinement); } static convertRecordFields(record) { const converted = Object.assign(record, { id: record._id, url: record._u, title: record._t }); delete converted._id; delete converted._u; delete converted._t; if (record._snippet) { converted.snippet = record._snippet; delete converted._snippet; } return converted; } static convertRefinement(navigation) { if (navigation.range) { navigation.min = Number.MAX_SAFE_INTEGER; navigation.max = Number.MIN_SAFE_INTEGER; navigation.refinements = navigation.refinements .map((ref) => { navigation.min = Math.min(navigation.min, ref.low); navigation.max = Math.max(navigation.max, ref.high); return (Object.assign({}, ref, { high: parseFloat(ref.high), low: parseFloat(ref.low) })); }); } return navigation; } } export class CloudBridge extends AbstractBridge { constructor(clientKey, customerId, config = {}) { super(config); this.clientKey = clientKey; this.baseUrl = `https://${customerId}.groupbycloud.com:443/api/v1`; this.bridgeUrl = this.baseUrl + SEARCH; this.refinementsUrl = this.bridgeUrl + REFINEMENTS; } augmentRequest(request) { return Object.assign(request, { clientKey: this.clientKey }); } } export class BrowserBridge extends AbstractBridge { constructor(customerId, https = false, config = {}) { super(config); const scheme = https ? 'https' : 'http'; const port = https ? ':443' : ''; this.baseUrl = config.proxyUrl || `${scheme}://${customerId}-cors.groupbycloud.com${port}/api/v1`; this.bridgeUrl = this.baseUrl + SEARCH; this.refinementsUrl = this.bridgeUrl + REFINEMENTS; } augmentRequest(request) { return request; } } //# sourceMappingURL=bridge.js.map