diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 1f4ba66e07e6f..1e1e35b08f9c9 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -4032,9 +4032,9 @@ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" }, "bignumber.js": { - "version": "2.4.0", - "resolved": "http://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", - "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.0.tgz", + "integrity": "sha512-hRv8jfqOBRnK1oGd0zDbfZv5blY3gy4QIsZZQ35mLCEMp/YdZmnrphBhX02SRKyLlohl+Sx6Ol7RlQhVLj/TAg==" }, "binary-extensions": { "version": "1.12.0", @@ -11876,6 +11876,13 @@ "stream-to-buffer": "^0.1.0", "tinycolor2": "^1.1.2", "url-regex": "^3.0.0" + }, + "dependencies": { + "bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=" + } } }, "jpeg-js": { diff --git a/superset/assets/package.json b/superset/assets/package.json index 7f57d1fab6b57..94fc251fde091 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -62,6 +62,7 @@ "@vx/responsive": "0.0.172", "@vx/scale": "^0.0.165", "abortcontroller-polyfill": "^1.1.9", + "bignumber.js": "^8.1.0", "bootstrap": "^3.3.6", "bootstrap-slider": "^10.0.0", "brace": "^0.11.1", diff --git a/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js b/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js new file mode 100644 index 0000000000000..1500a42437836 --- /dev/null +++ b/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import BigNumber from 'bignumber.js'; +import transform from 'src/chart/transformBigNumber'; + +describe('transformBigNumber', () => { + it('should transform BigNumber on its own', () => { + expect(transform(new BigNumber(123.456))).toBe(123.456); + }); + + it('should transform BigNumber in objects', () => { + expect(transform({ + foo: new BigNumber(123), + bar: 456, + baz: null, + })).toEqual({ foo: 123, bar: 456, baz: null }); + }); + + it('should transform BigNumber in arrays', () => { + expect(transform([ + { foo: new BigNumber(123) }, + { bar: 456 }, + ])).toEqual([{ foo: 123 }, { bar: 456 }]); + }); + + it('should transform BigNumber in nested structures', () => { + expect(transform([{ + x: new BigNumber(123), + y: [{ foo: new BigNumber(456) }, { bar: 'str' }], + z: { some: [new BigNumber(789)] }, + }])).toEqual([{ + x: 123, + y: [{ foo: 456 }, { bar: 'str' }], + z: { some: [789] }, + }]); + }); +}); diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index 8db91071ceee8..cffdfcb910de8 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -24,6 +24,7 @@ import { ChartProps } from '@superset-ui/chart'; import { Tooltip } from 'react-bootstrap'; import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger'; import SuperChart from '../visualizations/core/components/SuperChart'; +import transformBigNumber from './transformBigNumber'; const propTypes = { annotationData: PropTypes.object, @@ -67,6 +68,7 @@ class ChartRenderer extends React.Component { this.handleAddFilter = this.handleAddFilter.bind(this); this.handleRenderSuccess = this.handleRenderSuccess.bind(this); this.handleRenderFailure = this.handleRenderFailure.bind(this); + this.preTransformProps = this.preTransformProps.bind(this); } shouldComponentUpdate(nextProps, nextState) { @@ -150,6 +152,18 @@ class ChartRenderer extends React.Component { }); } + preTransformProps(chartProps) { + const payload = chartProps.payload; + const data = transformBigNumber(payload.data); + return new ChartProps({ + ...chartProps, + payload: { + ...payload, + data, + }, + }); + } + renderTooltip() { const { tooltip } = this.state; if (tooltip && tooltip.content) { @@ -193,6 +207,7 @@ class ChartRenderer extends React.Component { className={`${snakeCase(vizType)}`} chartType={vizType} chartProps={skipChartRendering ? null : this.prepareChartProps()} + preTransformProps={this.preTransformProps} onRenderSuccess={this.handleRenderSuccess} onRenderFailure={this.handleRenderFailure} /> diff --git a/superset/assets/src/chart/transformBigNumber.js b/superset/assets/src/chart/transformBigNumber.js new file mode 100644 index 0000000000000..dffa02a7f47ef --- /dev/null +++ b/superset/assets/src/chart/transformBigNumber.js @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// This method transforms any BigNumber object in the given payload to its +// 64-bit float representation. It is a temporary fix so charts receive +// floats instead of BigNumber instances in their props to properly render. +import BigNumber from 'bignumber.js'; + +export default function transform(payload) { + if (!payload) { + return payload; + } else if (BigNumber.isBigNumber(payload)) { + return payload.toNumber(); + } else if (payload.constructor === Object) { + for (const key in payload) { + if (payload.hasOwnProperty(key)) { + // Modify in place to prevent creating large payloads + // eslint-disable-next-line no-param-reassign + payload[key] = transform(payload[key]); + } + } + } else if (payload.constructor === Array) { + payload.forEach((elem, idx) => { + // Modify in place to prevent creating large payloads + // eslint-disable-next-line no-param-reassign + payload[idx] = transform(elem); + }); + } + return payload; +}