UNPKG

5.08 kBPlain TextView Raw
1// (C) 2007-2019 GoodData Corporation
2import get from "lodash/get";
3import { delay } from "./utils/promise";
4import { ApiResponse, ApiResponseError } from "./xhr";
5
6import { name as pkgName, version as pkgVersion } from "../package.json";
7
8/**
9 * Utility methods. Mostly private
10 *
11 * @module util
12 * @class util
13 *
14 */
15
16/**
17 * Gooddata-js package signature
18 * @private
19 */
20export const thisPackage = { name: pkgName, version: pkgVersion };
21
22/**
23 * Create getter function for accessing nested objects
24 *
25 * @param {String} path Target path to nested object
26 * @method getIn
27 * @private
28 */
29export const getIn = (path: string) => (object: any) => get(object, path);
30
31export interface IPollingOptions {
32 attempts?: number;
33 maxAttempts?: number;
34 pollStep?: number;
35}
36
37/**
38 * Helper for polling
39 *
40 * @param xhrRequest xhr module
41 * @param {String} uri
42 * @param {Function} isPollingDone
43 * @param {Object} options for polling (maxAttempts, pollStep)
44 * @private
45 */
46export const handlePolling = (
47 xhrRequest: any,
48 uri: string,
49 isPollingDone: (response: any) => boolean,
50 options: IPollingOptions = {},
51) => {
52 // TODO
53 const { attempts = 0, maxAttempts = 50, pollStep = 5000 } = options;
54
55 return xhrRequest(uri)
56 .then((r: any) => r.getData())
57 .then((response: any) => {
58 if (attempts > maxAttempts) {
59 return Promise.reject(new Error(response));
60 }
61 return isPollingDone(response)
62 ? Promise.resolve(response)
63 : delay(pollStep).then(() => {
64 return handlePolling(xhrRequest, uri, isPollingDone, {
65 ...options,
66 attempts: attempts + 1,
67 });
68 });
69 });
70};
71
72/**
73 * Helper for polling with header status
74 *
75 * @param xhrRequest xhr module
76 * @param {String} uri
77 * @param {Function} isPollingDone
78 * @param {Object} options for polling (maxAttempts, pollStep)
79 * @private
80 */
81export const handleHeadPolling = (
82 xhrRequest: any,
83 uri: string,
84 isPollingDone: (responseHeaders: Response, response: ApiResponse) => boolean,
85 options: IPollingOptions = {},
86) => {
87 const { attempts = 0, maxAttempts = 50, pollStep = 5000 } = options;
88
89 return xhrRequest(uri).then((response: any) => {
90 if (attempts > maxAttempts) {
91 return Promise.reject(new Error("Export timeout!!!"));
92 }
93 const responseHeaders = response.getHeaders();
94 if (isPollingDone(responseHeaders, response)) {
95 if (responseHeaders.status === 200) {
96 return Promise.resolve({ uri });
97 }
98 return Promise.reject(new ApiResponseError(response.statusText, response, response.getData()));
99 } else {
100 return delay(pollStep).then(() =>
101 handleHeadPolling(xhrRequest, uri, isPollingDone, {
102 ...options,
103 attempts: attempts + 1,
104 }),
105 );
106 }
107 });
108};
109
110/**
111 * Builds query string from plain object
112 * (Refactored from admin/routes.js)
113 *
114 * @param {Object} query parameters possibly including arrays inside
115 * @returns {string} querystring
116 */
117export function queryString(query: any) {
118 function getSingleParam(key: string, value: string) {
119 return Array.isArray(value)
120 ? value.map(item => `${encodeURIComponent(key)}=${encodeURIComponent(item)}`).join("&")
121 : `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
122 }
123
124 return query
125 ? `?${Object.keys(query)
126 .map(k => getSingleParam(k, query[k]))
127 .join("&")}`
128 : "";
129}
130
131/**
132 * Get all results from paged api by traversing all resulting pages
133 * This is usable for apis which support offset and limit (i.e. not those with next paging links)
134 *
135 * @param xhrGet xhr module
136 * @param {string} uri uri to be fetched, will append offset and limit for next pages
137 * @param {string} itemKey key under which to look for results (differs for different apis)
138 * @param {number} optional offset starting offset, default 0
139 * @param pagesData optional data to be pre-filled
140 */
141export function getAllPagesByOffsetLimit(
142 xhr: any,
143 uri: string,
144 itemKey: string,
145 offset: number = 0,
146 pagesData: any[] = [],
147) {
148 const PAGE_LIMIT = 100;
149 return new Promise((resolve: any, reject: any) => {
150 xhr.get(`${uri}?offset=${offset}&limit=${PAGE_LIMIT}`)
151 .then((r: any) => r.getData())
152 .then((dataObjects: any[]) => {
153 const projects = get(dataObjects, itemKey);
154 const data = pagesData.concat(projects.items);
155
156 const totalCount = get(projects, "paging.totalCount", 0);
157 const nextPage = offset + PAGE_LIMIT;
158 if (nextPage > totalCount) {
159 resolve(data);
160 } else {
161 resolve(getAllPagesByOffsetLimit(xhr, uri, itemKey, nextPage, data));
162 }
163 }, reject);
164 });
165}