UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

341 lines • 17.6 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); // (C) 2007-2020 GoodData Corporation var React = require("react"); var noop = require("lodash/noop"); var get = require("lodash/get"); var isEqual = require("lodash/isEqual"); var omit = require("lodash/omit"); var flatten = require("lodash/flatten"); var uniqBy = require("lodash/uniqBy"); var gooddata_js_1 = require("@gooddata/gooddata-js"); var typings_1 = require("@gooddata/typings"); var errorStates_1 = require("../../../constants/errorStates"); var errorHandlers_1 = require("../../../helpers/errorHandlers"); var react_intl_1 = require("react-intl"); var IntlWrapper_1 = require("../../core/base/IntlWrapper"); var LoadingComponent_1 = require("../../simple/LoadingComponent"); var ErrorComponent_1 = require("../../simple/ErrorComponent"); var RuntimeError_1 = require("../../../errors/RuntimeError"); var utils_1 = require("../../../helpers/utils"); var fixEmptyHeaderItems_1 = require("./utils/fixEmptyHeaderItems"); var escapeFileName = function (str) { return str && str.replace(/[\/\?<>\\:\*\|":]/g, ""); }; var defaultErrorHandler = function (error) { // if error was not placed in object, we couldnt see its properties in console (ie cause, responseText etc.) console.error("Error in execution:", { error: error }); // tslint:disable-line no-console }; exports.commonDefaultProps = { resultSpec: undefined, onError: defaultErrorHandler, onLoadingChanged: noop, ErrorComponent: ErrorComponent_1.ErrorComponent, LoadingComponent: LoadingComponent_1.LoadingComponent, afterRender: noop, pushData: noop, locale: "en-US", drillableItems: [], onExportReady: noop, onFiredDrillEvent: function () { return true; }, }; function visualizationLoadingHOC(InnerComponent, autoExecuteDataSource) { if (autoExecuteDataSource === void 0) { autoExecuteDataSource = true; } var LoadingHOCWrapped = /** @class */ (function (_super) { __extends(LoadingHOCWrapped, _super); function LoadingHOCWrapped(props) { var _this = _super.call(this, props) || this; _this.removePagePromise = function (promise) { var promiseIndex = _this.pagePromises.indexOf(promise); if (promiseIndex > -1) { _this.pagePromises = _this.pagePromises .slice(0, promiseIndex) .concat(_this.pagePromises.slice(promiseIndex + 1)); } }; _this.state = { isLoading: false, result: null, error: null, }; _this.sdk = props.sdk ? props.sdk.clone() : gooddata_js_1.factory(); utils_1.setTelemetryHeaders(_this.sdk, "LoadingHOCWrapped", props); _this.pagePromises = []; _this.hasUnmounted = false; _this.onLoadingChanged = _this.onLoadingChanged.bind(_this); _this.onDataTooLarge = _this.onDataTooLarge.bind(_this); _this.onNegativeValues = _this.onNegativeValues.bind(_this); _this.getPage = _this.getPage.bind(_this); _this.cancelPagePromises = _this.cancelPagePromises.bind(_this); _this.createExportFunction = _this.createExportFunction.bind(_this); _this.initSubject(); return _this; } LoadingHOCWrapped.prototype.componentDidMount = function () { var _a = this.props, dataSource = _a.dataSource, resultSpec = _a.resultSpec; if (autoExecuteDataSource) { this.initDataLoading(dataSource, resultSpec); } else { // without this, the ReportVisualization component doesn't assign executionId // and doesn't match results to executions and never stops loading this.onLoadingChanged({ isLoading: true }); } }; LoadingHOCWrapped.prototype.render = function () { var _a = this.state, result = _a.result, isLoading = _a.isLoading, error = _a.error; var intl = this.props.intl; var getPageProperty = autoExecuteDataSource ? {} : { getPage: this.getPage, cancelPagePromises: this.cancelPagePromises, }; // lower-level components do not need projectId var props = omit(this.props, ["projectId"]); return (React.createElement(InnerComponent, __assign({ key: "InnerComponent" }, props, { execution: result, onDataTooLarge: this.onDataTooLarge, onNegativeValues: this.onNegativeValues, error: error, isLoading: isLoading, intl: intl }, getPageProperty))); }; LoadingHOCWrapped.prototype.cancelPagePromises = function () { this.pagePromises = []; // dumping this array of getPage promises will result in effectively cancelling them }; LoadingHOCWrapped.prototype.getPage = function (resultSpec, limit, offset) { var _this = this; if (this.hasUnmounted) { return Promise.resolve(null); } this.setState({ error: null }); var pagePromise = this.createPagePromise(resultSpec, limit, offset); return pagePromise .then(errorHandlers_1.checkEmptyResult) .then(function (rawExecution) { var emptyHeaderString = "(" + _this.props.intl.formatMessage({ id: "visualization.emptyValue", }) + ")"; var executionResultWithResolvedEmptyValues = fixEmptyHeaderItems_1.fixEmptyHeaderItems(rawExecution.executionResult, emptyHeaderString); var result = __assign({}, rawExecution, { executionResult: executionResultWithResolvedEmptyValues }); var supportedDrillableItems = _this.getSupportedDrillableItems(result.executionResponse); // This returns only current page, // gooddata-js mergePages doesn't support discontinuous page ranges yet _this.setState({ result: result, error: null }); _this.props.pushData({ result: result, supportedDrillableItems: supportedDrillableItems, }); _this.onLoadingChanged({ isLoading: false }); _this.props.onExportReady(_this.createExportFunction(result)); // Pivot tables return result; }) .catch(function (error) { // only trigger errors on non-cancelled promises if (error.message !== errorStates_1.ErrorStates.CANCELLED) { _this.pushDataForError(error); _this.onError(errorHandlers_1.convertErrors(error)); } }); }; LoadingHOCWrapped.prototype.isDataReloadRequired = function (nextProps) { return (!gooddata_js_1.DataLayer.DataSourceUtils.dataSourcesMatch(this.props.dataSource, nextProps.dataSource) || !isEqual(this.props.resultSpec, nextProps.resultSpec)); }; LoadingHOCWrapped.prototype.componentWillReceiveProps = function (nextProps) { if (nextProps.sdk && this.props.sdk !== nextProps.sdk) { this.sdk = nextProps.sdk.clone(); utils_1.setTelemetryHeaders(this.sdk, "LoadingHOCWrapped", nextProps); } if (this.isDataReloadRequired(nextProps)) { if (autoExecuteDataSource) { var dataSource = nextProps.dataSource, resultSpec = nextProps.resultSpec; this.initDataLoading(dataSource, resultSpec); } else { this.onLoadingChanged({ isLoading: true }); } } }; LoadingHOCWrapped.prototype.componentWillUnmount = function () { this.hasUnmounted = true; this.subject.unsubscribe(); this.cancelPagePromises(); this.getPage = function () { return Promise.resolve(null); }; this.cancelPagePromises = noop; this.onLoadingChanged = noop; this.onError = noop; }; LoadingHOCWrapped.prototype.createPagePromise = function (resultSpec, limit, offset) { var _this = this; var pagePromise = this.props.dataSource.getPage(resultSpec, limit, offset); this.pagePromises.push(pagePromise); return pagePromise.then(function (result) { if (_this.isCancelled(pagePromise)) { _this.removePagePromise(pagePromise); return result; } else { throw new Error(errorStates_1.ErrorStates.CANCELLED); } }); }; LoadingHOCWrapped.prototype.isCancelled = function (pagePromise) { return this.pagePromises.includes(pagePromise); }; LoadingHOCWrapped.prototype.getAllAttributeHeaders = function (response) { return flatten(response.dimensions.map(function (dimension) { return dimension.headers; })) .filter(function (header) { return typings_1.Execution.isAttributeHeader(header); }) .map(function (header) { return header; }); }; LoadingHOCWrapped.prototype.getAllAttributes = function (response) { return uniqBy(this.getAllAttributeHeaders(response).map(function (attribute) { return ({ attribute: attribute.attributeHeader.formOf.uri, displayForm: attribute.attributeHeader.uri, }); }), function (attribute) { return attribute.displayForm; }); }; LoadingHOCWrapped.prototype.getAllMeasureHeaderItems = function (response) { return flatten(flatten(flatten(response.dimensions).map(function (dimension) { return dimension.headers; })) .filter(function (header) { return typings_1.Execution.isMeasureGroupHeader(header); }) .map(function (header) { return header.measureGroupHeader.items; })); }; LoadingHOCWrapped.prototype.getSupportedDrillableItems = function (response) { var attributes = this.getAllAttributes(response); return this.getAllMeasureHeaderItems(response).map(function (measure) { return ({ type: "measure", localIdentifier: measure.measureHeaderItem.localIdentifier, title: measure.measureHeaderItem.name, attributes: attributes, }); }); }; LoadingHOCWrapped.prototype.initSubject = function () { var _this = this; this.subject = gooddata_js_1.DataLayer.createSubject(function (result) { _this.setState({ result: result }); var supportedDrillableItems = _this.getSupportedDrillableItems(result.executionResponse); _this.props.pushData({ result: result, supportedDrillableItems: supportedDrillableItems }); _this.onLoadingChanged({ isLoading: false }); _this.props.onExportReady(_this.createExportFunction(result)); // Charts / Tables }, function (error) { return _this.onError(error); }); }; LoadingHOCWrapped.prototype.pushDataForError = function (error) { if (gooddata_js_1.TypeGuards.isApiExecutionResponseError(error)) { var supportedDrillableItems = this.getSupportedDrillableItems(error.executionResponse); this.props.pushData({ supportedDrillableItems: supportedDrillableItems }); } }; LoadingHOCWrapped.prototype.pushDataForNoContentResponse = function (error) { var _this = this; if (!errorHandlers_1.isApiResponseError(error.cause) || error.getMessage() !== errorStates_1.ErrorStates.NO_DATA) { return; } var response = error.cause.response.json(); response.then(function (r) { var supportedDrillableItems = _this.getSupportedDrillableItems(r.executionResponse); _this.props.pushData({ supportedDrillableItems: supportedDrillableItems }); }); }; LoadingHOCWrapped.prototype.onLoadingChanged = function (loadingState) { this.props.onLoadingChanged(loadingState); var isLoading = loadingState.isLoading; var state = { isLoading: isLoading }; if (isLoading) { state.error = null; } this.setState(state); }; LoadingHOCWrapped.prototype.onError = function (error, dataSource) { if (dataSource === void 0) { dataSource = this.props.dataSource; } if (gooddata_js_1.DataLayer.DataSourceUtils.dataSourcesMatch(this.props.dataSource, dataSource)) { this.setState({ error: error.getMessage() }); this.pushDataForNoContentResponse(error); this.onLoadingChanged({ isLoading: false }); this.props.onExportReady(this.createExportErrorFunction(error)); this.props.onError(error); } }; LoadingHOCWrapped.prototype.onDataTooLarge = function () { this.onError(new RuntimeError_1.RuntimeError(errorStates_1.ErrorStates.DATA_TOO_LARGE_TO_DISPLAY)); }; LoadingHOCWrapped.prototype.onNegativeValues = function () { this.onError(new RuntimeError_1.RuntimeError(errorStates_1.ErrorStates.NEGATIVE_VALUES)); }; LoadingHOCWrapped.prototype.initDataLoading = function (dataSource, resultSpec) { var _this = this; this.onLoadingChanged({ isLoading: true }); this.setState({ result: null }); var promise = dataSource .getData(resultSpec) .then(errorHandlers_1.checkEmptyResult) .catch(function (error) { _this.pushDataForError(error); throw errorHandlers_1.convertErrors(error); }); this.subject.next(promise); }; LoadingHOCWrapped.prototype.createExportFunction = function (execution) { var _this = this; return function (exportConfig) { var _a = _this.props, dataSource = _a.dataSource, exportTitle = _a.exportTitle, projectId = _a.projectId; if (!execution) { return Promise.reject(new Error("Unknown execution")); } if (!projectId) { return Promise.reject(new Error("Unknown projectId")); } var format = exportConfig.format, includeFilterContext = exportConfig.includeFilterContext, mergeHeaders = exportConfig.mergeHeaders, customTitle = exportConfig.title; var title = escapeFileName(customTitle || exportTitle) || "Untitled"; var exportConfigRequest = { format: format, mergeHeaders: mergeHeaders, title: title, }; if (includeFilterContext) { exportConfigRequest.showFilters = true; exportConfigRequest.afm = dataSource.getAfm(); } return _this.sdk.report.exportResult(projectId, get(execution, "executionResponse.links.executionResult"), exportConfigRequest); }; }; LoadingHOCWrapped.prototype.createExportErrorFunction = function (error) { return function (_exportConfig) { return Promise.reject(error); }; }; LoadingHOCWrapped.defaultProps = InnerComponent.defaultProps; return LoadingHOCWrapped; }(React.Component)); var IntlLoadingHOC = react_intl_1.injectIntl(LoadingHOCWrapped); return /** @class */ (function (_super) { __extends(LoadingHOC, _super); function LoadingHOC() { return _super !== null && _super.apply(this, arguments) || this; } LoadingHOC.prototype.render = function () { return (React.createElement(IntlWrapper_1.IntlWrapper, { locale: this.props.locale }, React.createElement(IntlLoadingHOC, __assign({}, this.props)))); }; return LoadingHOC; }(React.Component)); } exports.visualizationLoadingHOC = visualizationLoadingHOC; //# sourceMappingURL=VisualizationLoadingHOC.js.map