// (C) 2007-2019 GoodData Corporation
import get from "lodash/get";
import { delay } from "./utils/promise";
import { ApiResponse, ApiResponseError } from "./xhr";
import { name as pkgName, version as pkgVersion } from "../package.json";
/**
* Utility methods. Mostly private
*
* @module util
* @class util
*
*/
/**
* Gooddata-js package signature
* @private
*/
export const thisPackage = { name: pkgName, version: pkgVersion };
/**
* Create getter function for accessing nested objects
*
* @param {String} path Target path to nested object
* @method getIn
* @private
*/
export const getIn = (path: string) => (object: any) => get(object, path);
export interface IPollingOptions {
attempts?: number;
maxAttempts?: number;
pollStep?: number;
}
/**
* Helper for polling
*
* @param xhrRequest xhr module
* @param {String} uri
* @param {Function} isPollingDone
* @param {Object} options for polling (maxAttempts, pollStep)
* @private
*/
export const handlePolling = (
xhrRequest: any,
uri: string,
isPollingDone: (response: any) => boolean,
options: IPollingOptions = {},
) => {
// TODO
const { attempts = 0, maxAttempts = 50, pollStep = 5000 } = options;
return xhrRequest(uri)
.then((r: any) => r.getData())
.then((response: any) => {
if (attempts > maxAttempts) {
return Promise.reject(new Error(response));
}
return isPollingDone(response)
? Promise.resolve(response)
: delay(pollStep).then(() => {
return handlePolling(xhrRequest, uri, isPollingDone, {
...options,
attempts: attempts + 1,
});
});
});
};
/**
* Helper for polling with header status
*
* @param xhrRequest xhr module
* @param {String} uri
* @param {Function} isPollingDone
* @param {Object} options for polling (maxAttempts, pollStep)
* @private
*/
export const handleHeadPolling = (
xhrRequest: any,
uri: string,
isPollingDone: (responseHeaders: Response, response: ApiResponse) => boolean,
options: IPollingOptions = {},
) => {
const { attempts = 0, maxAttempts = 50, pollStep = 5000 } = options;
return xhrRequest(uri).then((response: any) => {
if (attempts > maxAttempts) {
return Promise.reject(new Error("Export timeout!!!"));
}
const responseHeaders = response.getHeaders();
if (isPollingDone(responseHeaders, response)) {
if (responseHeaders.status === 200) {
return Promise.resolve({ uri });
}
return Promise.reject(new ApiResponseError(response.statusText, response, response.getData()));
} else {
return delay(pollStep).then(() =>
handleHeadPolling(xhrRequest, uri, isPollingDone, {
...options,
attempts: attempts + 1,
}),
);
}
});
};
/**
* Builds query string from plain object
* (Refactored from admin/routes.js)
*
* @param {Object} query parameters possibly including arrays inside
* @returns {string} querystring
*/
export function queryString(query: any) {
function getSingleParam(key: string, value: string) {
return Array.isArray(value)
? value.map(item => `${encodeURIComponent(key)}=${encodeURIComponent(item)}`).join("&")
: `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
return query
? `?${Object.keys(query)
.map(k => getSingleParam(k, query[k]))
.join("&")}`
: "";
}
/**
* Get all results from paged api by traversing all resulting pages
* This is usable for apis which support offset and limit (i.e. not those with next paging links)
*
* @param xhrGet xhr module
* @param {string} uri uri to be fetched, will append offset and limit for next pages
* @param {string} itemKey key under which to look for results (differs for different apis)
* @param {number} optional offset starting offset, default 0
* @param pagesData optional data to be pre-filled
*/
export function getAllPagesByOffsetLimit(
xhr: any,
uri: string,
itemKey: string,
offset: number = 0,
pagesData: any[] = [],
) {
const PAGE_LIMIT = 100;
return new Promise((resolve: any, reject: any) => {
xhr.get(`${uri}?offset=${offset}&limit=${PAGE_LIMIT}`)
.then((r: any) => r.getData())
.then((dataObjects: any[]) => {
const projects = get(dataObjects, itemKey);
const data = pagesData.concat(projects.items);
const totalCount = get(projects, "paging.totalCount", 0);
const nextPage = offset + PAGE_LIMIT;
if (nextPage > totalCount) {
resolve(data);
} else {
resolve(getAllPagesByOffsetLimit(xhr, uri, itemKey, nextPage, data));
}
}, reject);
});
}