diff --git a/superset/assets/src/components/AlteredSliceTag.jsx b/superset/assets/src/components/AlteredSliceTag.jsx index 77cadd4d8..985b16f42 100644 --- a/superset/assets/src/components/AlteredSliceTag.jsx +++ b/superset/assets/src/components/AlteredSliceTag.jsx @@ -24,6 +24,7 @@ import { t } from '@superset-ui/translation'; import TooltipWrapper from './TooltipWrapper'; import { controls } from '../explore/controls'; import ModalTrigger from './ModalTrigger'; +import { safeStringify } from '../utils/safeStringify'; const propTypes = { origFormData: PropTypes.object.isRequired, @@ -112,7 +113,7 @@ export default class AlteredSliceTag extends React.Component { } else if (controls[key] && controls[key].type === 'BoundsControl') { return `Min: ${value[0]}, Max: ${value[1]}`; } else if (controls[key] && controls[key].type === 'CollectionControl') { - return value.map(v => JSON.stringify(v)).join(', '); + return value.map(v => safeStringify(v)).join(', '); } else if (typeof value === 'boolean') { return value ? 'true' : 'false'; } else if (value.constructor === Array) { @@ -120,7 +121,7 @@ export default class AlteredSliceTag extends React.Component { } else if (typeof value === 'string' || typeof value === 'number') { return value; } - return JSON.stringify(value); + return safeStringify(value); } renderRows() { diff --git a/superset/assets/src/dashboard/components/Header.jsx b/superset/assets/src/dashboard/components/Header.jsx index 8af3d3cb7..d0714f31b 100644 --- a/superset/assets/src/dashboard/components/Header.jsx +++ b/superset/assets/src/dashboard/components/Header.jsx @@ -33,6 +33,7 @@ import { SAVE_TYPE_OVERWRITE, DASHBOARD_POSITION_DATA_LIMIT, } from '../util/constants'; +import { safeStringify } from '../../utils/safeStringify'; const propTypes = { addSuccessToast: PropTypes.func.isRequired, @@ -166,11 +167,11 @@ class Header extends React.PureComponent { expanded_slices: expandedSlices, css, dashboard_title: dashboardTitle, - default_filters: JSON.stringify(filters), + default_filters: safeStringify(filters), }; // make sure positions data less than DB storage limitation: - const positionJSONLength = JSON.stringify(positions).length; + const positionJSONLength = safeStringify(positions).length; const limit = dashboardInfo.common.conf.SUPERSET_DASHBOARD_POSITION_DATA_LIMIT || DASHBOARD_POSITION_DATA_LIMIT; diff --git a/superset/assets/src/dashboard/components/SaveModal.jsx b/superset/assets/src/dashboard/components/SaveModal.jsx index 9091b1780..f9a7a6cc7 100644 --- a/superset/assets/src/dashboard/components/SaveModal.jsx +++ b/superset/assets/src/dashboard/components/SaveModal.jsx @@ -25,6 +25,7 @@ import { t } from '@superset-ui/translation'; import ModalTrigger from '../../components/ModalTrigger'; import Checkbox from '../../components/Checkbox'; import { SAVE_TYPE_OVERWRITE, SAVE_TYPE_NEWDASHBOARD } from '../util/constants'; +import { safeStringify } from '../../utils/safeStringify'; const propTypes = { addSuccessToast: PropTypes.func.isRequired, @@ -102,7 +103,7 @@ class SaveModal extends React.PureComponent { expanded_slices: expandedSlices, dashboard_title: saveType === SAVE_TYPE_NEWDASHBOARD ? newDashName : dashboardTitle, - default_filters: JSON.stringify(filters), + default_filters: safeStringify(filters), duplicate_slices: this.state.duplicateSlices, }; diff --git a/superset/assets/src/explore/exploreUtils.js b/superset/assets/src/explore/exploreUtils.js index ec3496aea..48bb60a4f 100644 --- a/superset/assets/src/explore/exploreUtils.js +++ b/superset/assets/src/explore/exploreUtils.js @@ -47,7 +47,7 @@ export function getAnnotationJsonUrl(slice_id, form_data, isNative) { const endpoint = isNative ? 'annotation_json' : 'slice_json'; return uri.pathname(`/superset/${endpoint}/${slice_id}`) .search({ - form_data: JSON.stringify(form_data, + form_data: safeStringify(form_data, (key, value) => value === null ? undefined : value), }).toString(); } @@ -119,7 +119,7 @@ export function getExploreUrlAndPayload({ // Building the querystring (search) part of the URI const search = uri.search(true); if (formData.slice_id) { - search.form_data = JSON.stringify({ slice_id: formData.slice_id }); + search.form_data = safeStringify({ slice_id: formData.slice_id }); } if (force) { search.force = 'true'; @@ -175,7 +175,7 @@ export function exportChart(formData, endpointType) { const data = document.createElement('input'); data.type = 'hidden'; data.name = 'form_data'; - data.value = JSON.stringify(payload); + data.value = safeStringify(payload); exploreForm.appendChild(data); document.body.appendChild(exploreForm); diff --git a/superset/assets/src/visualizations/Rose/Rose.js b/superset/assets/src/visualizations/Rose/Rose.js index e8a127871..1c18b81bb 100644 --- a/superset/assets/src/visualizations/Rose/Rose.js +++ b/superset/assets/src/visualizations/Rose/Rose.js @@ -24,6 +24,7 @@ import { CategoricalColorNamespace } from '@superset-ui/color'; import { getNumberFormatter } from '@superset-ui/number-format'; import { getTimeFormatter } from '@superset-ui/time-format'; import './Rose.css'; +import { safeStringify } from '../../utils/safeStringify'; const propTypes = { // Data is an object hashed by numeric value, perhaps timestamp @@ -307,7 +308,7 @@ function Rose(element, props) { let inTransition = false; const ae = roseWrap .selectAll('g') - .data(JSON.parse(JSON.stringify(arcSt.data))) // deep copy data state + .data(JSON.parse(safeStringify(arcSt.data))) // deep copy data state .enter() .append('g') .attr('class', 'segment') @@ -319,7 +320,7 @@ function Rose(element, props) { const labels = labelsWrap .selectAll('g') - .data(JSON.parse(JSON.stringify(arcSt.labels))) + .data(JSON.parse(safeStringify(arcSt.labels))) .enter() .append('g') .attr('class', 'roseLabel') @@ -333,7 +334,7 @@ function Rose(element, props) { const groupLabels = groupLabelsWrap .selectAll('g') - .data(JSON.parse(JSON.stringify(arcSt.groupLabels))) + .data(JSON.parse(safeStringify(arcSt.groupLabels))) .enter() .append('g'); diff --git a/superset/assets/src/visualizations/core/components/SuperChart.jsx b/superset/assets/src/visualizations/core/components/SuperChart.jsx index 45c2f0331..0c7e6e695 100644 --- a/superset/assets/src/visualizations/core/components/SuperChart.jsx +++ b/superset/assets/src/visualizations/core/components/SuperChart.jsx @@ -21,6 +21,7 @@ import PropTypes from 'prop-types'; import { createSelector } from 'reselect'; import { getChartComponentRegistry, getChartTransformPropsRegistry, ChartProps } from '@superset-ui/chart'; import createLoadableRenderer from './createLoadableRenderer'; +import { safeStringify } from '../../../utils/safeStringify'; const IDENTITY = x => x; @@ -133,7 +134,7 @@ class SuperChart extends React.PureComponent {
ERROR  chartType="{chartType}" — - {JSON.stringify(error)} + {safeStringify(error)}
); }