mirror of https://github.com/apache/superset.git
fix(viz-gallery): respect denylist in viz gallery (#22658)
This commit is contained in:
parent
1e3746be21
commit
08f45ef207
|
@ -31,13 +31,13 @@ import explore from 'src/explore/reducers/exploreReducer';
|
||||||
import sqlLab from 'src/SqlLab/reducers/sqlLab';
|
import sqlLab from 'src/SqlLab/reducers/sqlLab';
|
||||||
import localStorageUsageInKilobytes from 'src/SqlLab/reducers/localStorageUsage';
|
import localStorageUsageInKilobytes from 'src/SqlLab/reducers/localStorageUsage';
|
||||||
import reports from 'src/reports/reducers/reports';
|
import reports from 'src/reports/reducers/reports';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
const impressionId = (state = '') => state;
|
const impressionId = (state = '') => state;
|
||||||
|
|
||||||
const container = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}');
|
const common = { ...bootstrapData.common };
|
||||||
const common = { ...bootstrap.common };
|
const user = { ...bootstrapData.user };
|
||||||
const user = { ...bootstrap.user };
|
|
||||||
|
|
||||||
const noopReducer =
|
const noopReducer =
|
||||||
(initialState: unknown) =>
|
(initialState: unknown) =>
|
||||||
|
|
|
@ -25,5 +25,5 @@ configureTestingLibrary({
|
||||||
testIdAttribute: 'data-test',
|
testIdAttribute: 'data-test',
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.innerHTML = '<div id="app" data-bootstrap="{}"></div>';
|
document.body.innerHTML = '<div id="app" data-bootstrap=""></div>';
|
||||||
expect.extend(matchers);
|
expect.extend(matchers);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
FeatureFlag,
|
FeatureFlag,
|
||||||
} from 'src/featureFlags';
|
} from 'src/featureFlags';
|
||||||
import setupExtensions from 'src/setup/setupExtensions';
|
import setupExtensions from 'src/setup/setupExtensions';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import getInitialState from './reducers/getInitialState';
|
import getInitialState from './reducers/getInitialState';
|
||||||
import rootReducer from './reducers/index';
|
import rootReducer from './reducers/index';
|
||||||
import { initEnhancer } from '../reduxUtils';
|
import { initEnhancer } from '../reduxUtils';
|
||||||
|
@ -48,8 +49,7 @@ import { theme } from '../preamble';
|
||||||
setupApp();
|
setupApp();
|
||||||
setupExtensions();
|
setupExtensions();
|
||||||
|
|
||||||
const appContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapData = JSON.parse(appContainer.getAttribute('data-bootstrap'));
|
|
||||||
|
|
||||||
initFeatureFlags(bootstrapData.common.feature_flags);
|
initFeatureFlags(bootstrapData.common.feature_flags);
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,9 @@ import * as chrono from 'chrono-node';
|
||||||
import ModalTrigger, { ModalTriggerRef } from 'src/components/ModalTrigger';
|
import ModalTrigger, { ModalTriggerRef } from 'src/components/ModalTrigger';
|
||||||
import { Form, FormItem } from 'src/components/Form';
|
import { Form, FormItem } from 'src/components/Form';
|
||||||
import Button from 'src/components/Button';
|
import Button from 'src/components/Button';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
const appContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapData = JSON.parse(
|
|
||||||
appContainer?.getAttribute('data-bootstrap') || '{}',
|
|
||||||
);
|
|
||||||
const scheduledQueriesConf = bootstrapData?.common?.conf?.SCHEDULED_QUERIES;
|
const scheduledQueriesConf = bootstrapData?.common?.conf?.SCHEDULED_QUERIES;
|
||||||
|
|
||||||
const validators = {
|
const validators = {
|
||||||
|
|
|
@ -74,6 +74,7 @@ import {
|
||||||
} from 'src/utils/localStorageHelpers';
|
} from 'src/utils/localStorageHelpers';
|
||||||
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
||||||
import { EmptyStateBig } from 'src/components/EmptyState';
|
import { EmptyStateBig } from 'src/components/EmptyState';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import TemplateParamsEditor from '../TemplateParamsEditor';
|
import TemplateParamsEditor from '../TemplateParamsEditor';
|
||||||
import SouthPane from '../SouthPane';
|
import SouthPane from '../SouthPane';
|
||||||
|
@ -86,10 +87,7 @@ import AceEditorWrapper from '../AceEditorWrapper';
|
||||||
import RunQueryActionButton from '../RunQueryActionButton';
|
import RunQueryActionButton from '../RunQueryActionButton';
|
||||||
import QueryLimitSelect from '../QueryLimitSelect';
|
import QueryLimitSelect from '../QueryLimitSelect';
|
||||||
|
|
||||||
const appContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapData = JSON.parse(
|
|
||||||
appContainer.getAttribute('data-bootstrap') || '{}',
|
|
||||||
);
|
|
||||||
const validatorMap =
|
const validatorMap =
|
||||||
bootstrapData?.common?.conf?.SQL_VALIDATORS_BY_ENGINE || {};
|
bootstrapData?.common?.conf?.SQL_VALIDATORS_BY_ENGINE || {};
|
||||||
const scheduledQueriesConf = bootstrapData?.common?.conf?.SCHEDULED_QUERIES;
|
const scheduledQueriesConf = bootstrapData?.common?.conf?.SCHEDULED_QUERIES;
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { Tooltip } from 'src/components/Tooltip';
|
||||||
import { detectOS } from 'src/utils/common';
|
import { detectOS } from 'src/utils/common';
|
||||||
import * as Actions from 'src/SqlLab/actions/sqlLab';
|
import * as Actions from 'src/SqlLab/actions/sqlLab';
|
||||||
import { EmptyStateBig } from 'src/components/EmptyState';
|
import { EmptyStateBig } from 'src/components/EmptyState';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import SqlEditor from '../SqlEditor';
|
import SqlEditor from '../SqlEditor';
|
||||||
import SqlEditorTabHeader from '../SqlEditorTabHeader';
|
import SqlEditorTabHeader from '../SqlEditorTabHeader';
|
||||||
|
|
||||||
|
@ -106,13 +107,10 @@ class TabbedSqlEditors extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge post form data with GET search params
|
// merge post form data with GET search params
|
||||||
// Hack: this data should be comming from getInitialState
|
// Hack: this data should be coming from getInitialState
|
||||||
// but for some reason this data isn't being passed properly through
|
// but for some reason this data isn't being passed properly through
|
||||||
// the reducer.
|
// the reducer.
|
||||||
const appContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapData = JSON.parse(
|
|
||||||
appContainer?.getAttribute('data-bootstrap') || '{}',
|
|
||||||
);
|
|
||||||
const query = {
|
const query = {
|
||||||
...bootstrapData.requested_query,
|
...bootstrapData.requested_query,
|
||||||
...URI(window.location).search(true),
|
...URI(window.location).search(true),
|
||||||
|
|
|
@ -84,7 +84,7 @@ async function getWrapper(user = mockUser) {
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
test('renders a select and a VizTypeControl', async () => {
|
test('renders a select and a VizTypeGallery', async () => {
|
||||||
const wrapper = await getWrapper();
|
const wrapper = await getWrapper();
|
||||||
expect(wrapper.find(AsyncSelect)).toExist();
|
expect(wrapper.find(AsyncSelect)).toExist();
|
||||||
expect(wrapper.find(VizTypeGallery)).toExist();
|
expect(wrapper.find(VizTypeGallery)).toExist();
|
||||||
|
|
|
@ -39,6 +39,7 @@ import VizTypeGallery, {
|
||||||
} from 'src/explore/components/controls/VizTypeControl/VizTypeGallery';
|
} from 'src/explore/components/controls/VizTypeControl/VizTypeGallery';
|
||||||
import { findPermission } from 'src/utils/findPermission';
|
import { findPermission } from 'src/utils/findPermission';
|
||||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
type Dataset = {
|
type Dataset = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -62,6 +63,9 @@ export type AddSliceContainerState = {
|
||||||
const ESTIMATED_NAV_HEIGHT = 56;
|
const ESTIMATED_NAV_HEIGHT = 56;
|
||||||
const ELEMENTS_EXCEPT_VIZ_GALLERY = ESTIMATED_NAV_HEIGHT + 250;
|
const ELEMENTS_EXCEPT_VIZ_GALLERY = ESTIMATED_NAV_HEIGHT + 250;
|
||||||
|
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
const denyList: string[] = bootstrapData.common.conf.VIZ_TYPE_DENYLIST || [];
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
${({ theme }) => `
|
${({ theme }) => `
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
@ -395,6 +399,7 @@ export class AddSliceContainer extends React.PureComponent<
|
||||||
description={
|
description={
|
||||||
<StyledStepDescription>
|
<StyledStepDescription>
|
||||||
<VizTypeGallery
|
<VizTypeGallery
|
||||||
|
denyList={denyList}
|
||||||
className="viz-gallery"
|
className="viz-gallery"
|
||||||
onChange={this.changeVizType}
|
onChange={this.changeVizType}
|
||||||
onDoubleClick={this.onVizTypeDoubleClick}
|
onDoubleClick={this.onVizTypeDoubleClick}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { isFeatureEnabled, t, FeatureFlag } from '@superset-ui/core';
|
||||||
|
|
||||||
import { PluginContext } from 'src/components/DynamicPlugins';
|
import { PluginContext } from 'src/components/DynamicPlugins';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import getChartIdsFromLayout from '../util/getChartIdsFromLayout';
|
import getChartIdsFromLayout from '../util/getChartIdsFromLayout';
|
||||||
import getLayoutComponentFromChartId from '../util/getLayoutComponentFromChartId';
|
import getLayoutComponentFromChartId from '../util/getLayoutComponentFromChartId';
|
||||||
import DashboardBuilder from './DashboardBuilder/DashboardBuilder';
|
import DashboardBuilder from './DashboardBuilder/DashboardBuilder';
|
||||||
|
@ -97,8 +98,7 @@ class Dashboard extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const appContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapData = appContainer?.getAttribute('data-bootstrap') || '{}';
|
|
||||||
const { dashboardState, layout } = this.props;
|
const { dashboardState, layout } = this.props;
|
||||||
const eventData = {
|
const eventData = {
|
||||||
is_soft_navigation: Logger.timeOriginOffset > 0,
|
is_soft_navigation: Logger.timeOriginOffset > 0,
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import { store } from '../views/store';
|
import { store } from '../views/store';
|
||||||
import { bootstrapData } from '../preamble';
|
|
||||||
import { getDashboardPermalink as getDashboardPermalinkUtil } from '../utils/urlUtils';
|
import { getDashboardPermalink as getDashboardPermalinkUtil } from '../utils/urlUtils';
|
||||||
|
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
|
||||||
type Size = {
|
type Size = {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|
|
@ -21,7 +21,7 @@ import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter as Router, Route } from 'react-router-dom';
|
import { BrowserRouter as Router, Route } from 'react-router-dom';
|
||||||
import { makeApi, t, logging } from '@superset-ui/core';
|
import { makeApi, t, logging } from '@superset-ui/core';
|
||||||
import Switchboard from '@superset-ui/switchboard';
|
import Switchboard from '@superset-ui/switchboard';
|
||||||
import { bootstrapData } from 'src/preamble';
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import setupClient from 'src/setup/setupClient';
|
import setupClient from 'src/setup/setupClient';
|
||||||
import { RootContextProviders } from 'src/views/RootContextProviders';
|
import { RootContextProviders } from 'src/views/RootContextProviders';
|
||||||
import { store, USER_LOADED } from 'src/views/store';
|
import { store, USER_LOADED } from 'src/views/store';
|
||||||
|
@ -33,6 +33,7 @@ import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||||
import { embeddedApi } from './api';
|
import { embeddedApi } from './api';
|
||||||
|
|
||||||
const debugMode = process.env.WEBPACK_MODE === 'development';
|
const debugMode = process.env.WEBPACK_MODE === 'development';
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
|
||||||
function log(...info: unknown[]) {
|
function log(...info: unknown[]) {
|
||||||
if (debugMode) {
|
if (debugMode) {
|
||||||
|
|
|
@ -40,7 +40,7 @@ import {
|
||||||
} from '@superset-ui/plugin-chart-echarts';
|
} from '@superset-ui/plugin-chart-echarts';
|
||||||
import TableChartPlugin from '@superset-ui/plugin-chart-table';
|
import TableChartPlugin from '@superset-ui/plugin-chart-table';
|
||||||
import { LineChartPlugin } from '@superset-ui/preset-chart-xy';
|
import { LineChartPlugin } from '@superset-ui/preset-chart-xy';
|
||||||
import TimeTableChartPlugin from '../../../../visualizations/TimeTable';
|
import TimeTableChartPlugin from 'src/visualizations/TimeTable';
|
||||||
import VizTypeControl, { VIZ_TYPE_CONTROL_TEST_ID } from './index';
|
import VizTypeControl, { VIZ_TYPE_CONTROL_TEST_ID } from './index';
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
|
@ -50,6 +50,7 @@ interface VizTypeGalleryProps {
|
||||||
onDoubleClick: () => void;
|
onDoubleClick: () => void;
|
||||||
selectedViz: string | null;
|
selectedViz: string | null;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
denyList: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type VizEntry = {
|
type VizEntry = {
|
||||||
|
@ -506,6 +507,7 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
|
||||||
const chartMetadata: VizEntry[] = useMemo(() => {
|
const chartMetadata: VizEntry[] = useMemo(() => {
|
||||||
const result = Object.entries(mountedPluginMetadata)
|
const result = Object.entries(mountedPluginMetadata)
|
||||||
.map(([key, value]) => ({ key, value }))
|
.map(([key, value]) => ({ key, value }))
|
||||||
|
.filter(({ key }) => !props.denyList.includes(key))
|
||||||
.filter(
|
.filter(
|
||||||
({ value }) =>
|
({ value }) =>
|
||||||
nativeFilterGate(value.behaviors || []) && !value.deprecated,
|
nativeFilterGate(value.behaviors || []) && !value.deprecated,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {
|
||||||
import { usePluginContext } from 'src/components/DynamicPlugins';
|
import { usePluginContext } from 'src/components/DynamicPlugins';
|
||||||
import Modal from 'src/components/Modal';
|
import Modal from 'src/components/Modal';
|
||||||
import { noOp } from 'src/utils/common';
|
import { noOp } from 'src/utils/common';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import VizTypeGallery, {
|
import VizTypeGallery, {
|
||||||
MAX_ADVISABLE_VIZ_GALLERY_WIDTH,
|
MAX_ADVISABLE_VIZ_GALLERY_WIDTH,
|
||||||
} from './VizTypeGallery';
|
} from './VizTypeGallery';
|
||||||
|
@ -41,6 +42,8 @@ interface VizTypeControlProps {
|
||||||
isModalOpenInit?: boolean;
|
isModalOpenInit?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
const denyList: string[] = bootstrapData.common.conf.VIZ_TYPE_DENYLIST || [];
|
||||||
const metadataRegistry = getChartMetadataRegistry();
|
const metadataRegistry = getChartMetadataRegistry();
|
||||||
|
|
||||||
export const VIZ_TYPE_CONTROL_TEST_ID = 'viz-type-control';
|
export const VIZ_TYPE_CONTROL_TEST_ID = 'viz-type-control';
|
||||||
|
@ -141,6 +144,7 @@ const VizTypeControl = ({
|
||||||
selectedViz={selectedViz}
|
selectedViz={selectedViz}
|
||||||
onChange={setSelectedViz}
|
onChange={setSelectedViz}
|
||||||
onDoubleClick={onSubmit}
|
onDoubleClick={onSubmit}
|
||||||
|
denyList={denyList}
|
||||||
/>
|
/>
|
||||||
</UnpaddedModal>
|
</UnpaddedModal>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
logging,
|
logging,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { SupersetError } from 'src/components/ErrorMessage/types';
|
import { SupersetError } from 'src/components/ErrorMessage/types';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import { FeatureFlag, isFeatureEnabled } from '../featureFlags';
|
import { FeatureFlag, isFeatureEnabled } from '../featureFlags';
|
||||||
import {
|
import {
|
||||||
getClientErrorObject,
|
getClientErrorObject,
|
||||||
|
@ -235,21 +236,7 @@ export const init = (appConfig?: AppConfig) => {
|
||||||
retriesByJobId = {};
|
retriesByJobId = {};
|
||||||
lastReceivedEventId = null;
|
lastReceivedEventId = null;
|
||||||
|
|
||||||
if (appConfig) {
|
config = appConfig || getBootstrapData().config;
|
||||||
config = appConfig;
|
|
||||||
} else {
|
|
||||||
// load bootstrap data from DOM
|
|
||||||
const appContainer = document.getElementById('app');
|
|
||||||
if (appContainer) {
|
|
||||||
const bootstrapData = JSON.parse(
|
|
||||||
appContainer?.getAttribute('data-bootstrap') || '{}',
|
|
||||||
);
|
|
||||||
config = bootstrapData?.common?.conf;
|
|
||||||
} else {
|
|
||||||
config = {};
|
|
||||||
logging.warn('asyncEvent: app config data not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transport = config.GLOBAL_ASYNC_QUERIES_TRANSPORT || TRANSPORT_POLLING;
|
transport = config.GLOBAL_ASYNC_QUERIES_TRANSPORT || TRANSPORT_POLLING;
|
||||||
pollingDelayMs = config.GLOBAL_ASYNC_QUERIES_POLLING_DELAY || 500;
|
pollingDelayMs = config.GLOBAL_ASYNC_QUERIES_POLLING_DELAY || 500;
|
||||||
|
|
||||||
|
|
|
@ -26,47 +26,34 @@ import setupClient from './setup/setupClient';
|
||||||
import setupColors from './setup/setupColors';
|
import setupColors from './setup/setupColors';
|
||||||
import setupFormatters from './setup/setupFormatters';
|
import setupFormatters from './setup/setupFormatters';
|
||||||
import setupDashboardComponents from './setup/setupDasboardComponents';
|
import setupDashboardComponents from './setup/setupDasboardComponents';
|
||||||
import { BootstrapData, User } from './types/bootstrapTypes';
|
import { User } from './types/bootstrapTypes';
|
||||||
import { initFeatureFlags } from './featureFlags';
|
import { initFeatureFlags } from './featureFlags';
|
||||||
import { DEFAULT_COMMON_BOOTSTRAP_DATA } from './constants';
|
import getBootstrapData from './utils/getBootstrapData';
|
||||||
|
|
||||||
if (process.env.WEBPACK_MODE === 'development') {
|
if (process.env.WEBPACK_MODE === 'development') {
|
||||||
setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false });
|
setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-mutable-exports
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
export let bootstrapData: BootstrapData = {
|
const bootstrapData = getBootstrapData();
|
||||||
common: {
|
|
||||||
...DEFAULT_COMMON_BOOTSTRAP_DATA,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure translation
|
// Configure translation
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
const root = document.getElementById('app');
|
configure({ languagePack: bootstrapData.common.language_pack });
|
||||||
bootstrapData = root
|
moment.locale(bootstrapData.common.locale);
|
||||||
? JSON.parse(root.getAttribute('data-bootstrap') || '{}')
|
|
||||||
: {};
|
|
||||||
if (bootstrapData?.common?.language_pack) {
|
|
||||||
const languagePack = bootstrapData.common.language_pack;
|
|
||||||
configure({ languagePack });
|
|
||||||
moment.locale(bootstrapData.common.locale);
|
|
||||||
} else {
|
|
||||||
configure();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
configure();
|
configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure feature flags
|
// Configure feature flags
|
||||||
initFeatureFlags(bootstrapData?.common?.feature_flags);
|
initFeatureFlags(bootstrapData.common.feature_flags);
|
||||||
|
|
||||||
// Setup SupersetClient
|
// Setup SupersetClient
|
||||||
setupClient();
|
setupClient();
|
||||||
|
|
||||||
setupColors(
|
setupColors(
|
||||||
bootstrapData?.common?.extra_categorical_color_schemes,
|
bootstrapData.common.extra_categorical_color_schemes,
|
||||||
bootstrapData?.common?.extra_sequential_color_schemes,
|
bootstrapData.common.extra_sequential_color_schemes,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup number formatters
|
// Setup number formatters
|
||||||
|
@ -76,7 +63,7 @@ setupDashboardComponents();
|
||||||
|
|
||||||
export const theme = merge(
|
export const theme = merge(
|
||||||
supersetTheme,
|
supersetTheme,
|
||||||
bootstrapData?.common?.theme_overrides ?? {},
|
bootstrapData.common.theme_overrides ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
const getMe = makeApi<void, User>({
|
const getMe = makeApi<void, User>({
|
||||||
|
|
|
@ -30,14 +30,12 @@ import setupApp from 'src/setup/setupApp';
|
||||||
import setupExtensions from 'src/setup/setupExtensions';
|
import setupExtensions from 'src/setup/setupExtensions';
|
||||||
import { theme } from 'src/preamble';
|
import { theme } from 'src/preamble';
|
||||||
import ToastContainer from 'src/components/MessageToasts/ToastContainer';
|
import ToastContainer from 'src/components/MessageToasts/ToastContainer';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
setupApp();
|
setupApp();
|
||||||
setupExtensions();
|
setupExtensions();
|
||||||
|
|
||||||
const profileViewContainer = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrap = JSON.parse(
|
|
||||||
profileViewContainer?.getAttribute('data-bootstrap') ?? '{}',
|
|
||||||
);
|
|
||||||
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
combineReducers({
|
combineReducers({
|
||||||
|
@ -51,7 +49,7 @@ const Application = () => (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
<App user={bootstrap.user} />
|
<App user={bootstrapData.user} />
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
||||||
import { t, styled } from '@superset-ui/core';
|
import { t, styled } from '@superset-ui/core';
|
||||||
import { Row, Col } from 'src/components';
|
import { Row, Col } from 'src/components';
|
||||||
import Tabs from 'src/components/Tabs';
|
import Tabs from 'src/components/Tabs';
|
||||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
import { BootstrapUser } from 'src/types/bootstrapTypes';
|
||||||
import Favorites from './Favorites';
|
import Favorites from './Favorites';
|
||||||
import UserInfo from './UserInfo';
|
import UserInfo from './UserInfo';
|
||||||
import Security from './Security';
|
import Security from './Security';
|
||||||
|
@ -28,7 +28,7 @@ import RecentActivity from './RecentActivity';
|
||||||
import CreatedContent from './CreatedContent';
|
import CreatedContent from './CreatedContent';
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
user: UserWithPermissionsAndRoles;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledTabPane = styled(Tabs.TabPane)`
|
const StyledTabPane = styled(Tabs.TabPane)`
|
||||||
|
|
|
@ -22,13 +22,13 @@ import { t } from '@superset-ui/core';
|
||||||
|
|
||||||
import TableLoader from 'src/components/TableLoader';
|
import TableLoader from 'src/components/TableLoader';
|
||||||
import {
|
import {
|
||||||
User,
|
BootstrapUser,
|
||||||
DashboardResponse,
|
|
||||||
ChartResponse,
|
ChartResponse,
|
||||||
|
DashboardResponse,
|
||||||
} from 'src/types/bootstrapTypes';
|
} from 'src/types/bootstrapTypes';
|
||||||
|
|
||||||
interface CreatedContentProps {
|
interface CreatedContentProps {
|
||||||
user: User;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreatedContent extends React.PureComponent<CreatedContentProps> {
|
class CreatedContent extends React.PureComponent<CreatedContentProps> {
|
||||||
|
|
|
@ -20,13 +20,12 @@ import React from 'react';
|
||||||
import rison from 'rison';
|
import rison from 'rison';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { t } from '@superset-ui/core';
|
import { t } from '@superset-ui/core';
|
||||||
|
import { DashboardResponse, BootstrapUser } from 'src/types/bootstrapTypes';
|
||||||
import TableLoader from '../../components/TableLoader';
|
import TableLoader from '../../components/TableLoader';
|
||||||
import { Slice } from '../types';
|
import { Slice } from '../types';
|
||||||
import { User, DashboardResponse } from '../../types/bootstrapTypes';
|
|
||||||
|
|
||||||
interface FavoritesProps {
|
interface FavoritesProps {
|
||||||
user: User;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Favorites extends React.PureComponent<FavoritesProps> {
|
export default class Favorites extends React.PureComponent<FavoritesProps> {
|
||||||
|
@ -40,7 +39,7 @@ export default class Favorites extends React.PureComponent<FavoritesProps> {
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<TableLoader
|
<TableLoader
|
||||||
dataEndpoint={`/superset/fave_slices/${this.props.user.userId}/`}
|
dataEndpoint={`/superset/fave_slices/${this.props.user?.userId}/`}
|
||||||
className="table-condensed"
|
className="table-condensed"
|
||||||
columns={['slice', 'creator', 'favorited']}
|
columns={['slice', 'creator', 'favorited']}
|
||||||
mutator={mutator}
|
mutator={mutator}
|
||||||
|
|
|
@ -21,10 +21,10 @@ import moment from 'moment';
|
||||||
|
|
||||||
import TableLoader from '../../components/TableLoader';
|
import TableLoader from '../../components/TableLoader';
|
||||||
import { Activity } from '../types';
|
import { Activity } from '../types';
|
||||||
import { User } from '../../types/bootstrapTypes';
|
import { BootstrapUser } from '../../types/bootstrapTypes';
|
||||||
|
|
||||||
interface RecentActivityProps {
|
interface RecentActivityProps {
|
||||||
user: User;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RecentActivity({ user }: RecentActivityProps) {
|
export default function RecentActivity({ user }: RecentActivityProps) {
|
||||||
|
@ -45,7 +45,7 @@ export default function RecentActivity({ user }: RecentActivityProps) {
|
||||||
className="table-condensed"
|
className="table-condensed"
|
||||||
mutator={mutator}
|
mutator={mutator}
|
||||||
sortable
|
sortable
|
||||||
dataEndpoint={`/superset/recent_activity/${user.userId}/?limit=${rowLimit}`}
|
dataEndpoint={`/superset/recent_activity/${user?.userId}/?limit=${rowLimit}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,10 +21,10 @@ import Badge from 'src/components/Badge';
|
||||||
import { t } from '@superset-ui/core';
|
import { t } from '@superset-ui/core';
|
||||||
|
|
||||||
import Label from 'src/components/Label';
|
import Label from 'src/components/Label';
|
||||||
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
|
import { BootstrapUser } from 'src/types/bootstrapTypes';
|
||||||
|
|
||||||
interface SecurityProps {
|
interface SecurityProps {
|
||||||
user: UserWithPermissionsAndRoles;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Security({ user }: SecurityProps) {
|
export default function Security({ user }: SecurityProps) {
|
||||||
|
@ -32,15 +32,16 @@ export default function Security({ user }: SecurityProps) {
|
||||||
<div>
|
<div>
|
||||||
<div className="roles">
|
<div className="roles">
|
||||||
<h4>
|
<h4>
|
||||||
{t('Roles')} <Badge count={Object.keys(user.roles).length} showZero />
|
{t('Roles')}{' '}
|
||||||
|
<Badge count={Object.keys(user?.roles || {}).length} showZero />
|
||||||
</h4>
|
</h4>
|
||||||
{Object.keys(user.roles).map(role => (
|
{Object.keys(user?.roles || {}).map(role => (
|
||||||
<Label key={role}>{role}</Label>
|
<Label key={role}>{role}</Label>
|
||||||
))}
|
))}
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
<div className="databases">
|
<div className="databases">
|
||||||
{user.permissions.database_access && (
|
{user?.permissions.database_access && (
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
{t('Databases')}{' '}
|
{t('Databases')}{' '}
|
||||||
|
@ -54,7 +55,7 @@ export default function Security({ user }: SecurityProps) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="datasources">
|
<div className="datasources">
|
||||||
{user.permissions.datasource_access && (
|
{user?.permissions.datasource_access && (
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
{t('Datasets')}{' '}
|
{t('Datasets')}{' '}
|
||||||
|
|
|
@ -20,10 +20,10 @@ import React from 'react';
|
||||||
import Gravatar from 'react-gravatar';
|
import Gravatar from 'react-gravatar';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { t, styled } from '@superset-ui/core';
|
import { t, styled } from '@superset-ui/core';
|
||||||
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
|
import { BootstrapUser } from 'src/types/bootstrapTypes';
|
||||||
|
|
||||||
interface UserInfoProps {
|
interface UserInfoProps {
|
||||||
user: UserWithPermissionsAndRoles;
|
user: BootstrapUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
|
@ -37,7 +37,7 @@ export default function UserInfo({ user }: UserInfoProps) {
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<a href="https://en.gravatar.com/">
|
<a href="https://en.gravatar.com/">
|
||||||
<Gravatar
|
<Gravatar
|
||||||
email={user.email}
|
email={user?.email}
|
||||||
width="100%"
|
width="100%"
|
||||||
height=""
|
height=""
|
||||||
size={220}
|
size={220}
|
||||||
|
@ -51,29 +51,30 @@ export default function UserInfo({ user }: UserInfoProps) {
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h3>
|
<h3>
|
||||||
<strong>
|
<strong>
|
||||||
{user.firstName} {user.lastName}
|
{user?.firstName} {user?.lastName}
|
||||||
</strong>
|
</strong>
|
||||||
</h3>
|
</h3>
|
||||||
<h4 className="username">
|
<h4 className="username">
|
||||||
<i className="fa fa-user-o" /> {user.username}
|
<i className="fa fa-user-o" /> {user?.username}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<p>
|
<p>
|
||||||
<i className="fa fa-clock-o" data-test="clock-icon-test" />{' '}
|
<i className="fa fa-clock-o" data-test="clock-icon-test" />{' '}
|
||||||
{t('joined')} {moment(user.createdOn, 'YYYYMMDD').fromNow()}
|
{t('joined')} {moment(user?.createdOn, 'YYYYMMDD').fromNow()}
|
||||||
</p>
|
</p>
|
||||||
<p className="email">
|
<p className="email">
|
||||||
<i className="fa fa-envelope-o" /> {user.email}
|
<i className="fa fa-envelope-o" /> {user?.email}
|
||||||
</p>
|
</p>
|
||||||
<p className="roles">
|
<p className="roles">
|
||||||
<i className="fa fa-lock" /> {Object.keys(user.roles).join(', ')}
|
<i className="fa fa-lock" />{' '}
|
||||||
|
{Object.keys(user?.roles || {}).join(', ')}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<i className="fa fa-key" />
|
<i className="fa fa-key" />
|
||||||
|
|
||||||
<span className="text-muted">{t('id:')}</span>
|
<span className="text-muted">{t('id:')}</span>
|
||||||
<span className="user-id">{user.userId}</span>
|
<span className="user-id">{user?.userId}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|
|
@ -21,11 +21,10 @@ import ReactDom from 'react-dom';
|
||||||
import Form from 'react-jsonschema-form';
|
import Form from 'react-jsonschema-form';
|
||||||
import { interpolate } from 'src/showSavedQuery/utils';
|
import { interpolate } from 'src/showSavedQuery/utils';
|
||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
const scheduleInfoContainer = document.getElementById('schedule-info');
|
const scheduleInfoContainer = document.getElementById('schedule-info');
|
||||||
const bootstrapData = JSON.parse(
|
const bootstrapData = getBootstrapData();
|
||||||
scheduleInfoContainer.getAttribute('data-bootstrap'),
|
|
||||||
);
|
|
||||||
const config = bootstrapData.common.conf.SCHEDULED_QUERIES;
|
const config = bootstrapData.common.conf.SCHEDULED_QUERIES;
|
||||||
const { query } = bootstrapData.common;
|
const { query } = bootstrapData.common;
|
||||||
const scheduleInfo = query.extra_json.schedule_info;
|
const scheduleInfo = query.extra_json.schedule_info;
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BootstrapData } from 'src/types/bootstrapTypes';
|
import { BootstrapData } from 'src/types/bootstrapTypes';
|
||||||
|
import { DEFAULT_BOOTSTRAP_DATA } from 'src/constants';
|
||||||
|
|
||||||
export default function getBootstrapData(): BootstrapData {
|
export default function getBootstrapData(): BootstrapData {
|
||||||
const appContainer = document.getElementById('app');
|
const appContainer = document.getElementById('app');
|
||||||
const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
|
const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
|
||||||
return dataBootstrap ? JSON.parse(dataBootstrap) : {};
|
return dataBootstrap ? JSON.parse(dataBootstrap) : DEFAULT_BOOTSTRAP_DATA;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
isFeatureEnabled,
|
isFeatureEnabled,
|
||||||
FeatureFlag,
|
FeatureFlag,
|
||||||
} from 'src/featureFlags';
|
} from 'src/featureFlags';
|
||||||
|
import getBootstrapData from './getBootstrapData';
|
||||||
|
|
||||||
function getDomainsConfig() {
|
function getDomainsConfig() {
|
||||||
const appContainer = document.getElementById('app');
|
const appContainer = document.getElementById('app');
|
||||||
|
@ -38,11 +39,11 @@ function getDomainsConfig() {
|
||||||
return Array.from(availableDomains);
|
return Array.from(availableDomains);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrapData = JSON.parse(appContainer.getAttribute('data-bootstrap'));
|
const bootstrapData = getBootstrapData();
|
||||||
// this module is a little special, it may be loaded before index.jsx,
|
// this module is a little special, it may be loaded before index.jsx,
|
||||||
// where window.featureFlags get initialized
|
// where window.featureFlags get initialized
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
initFeatureFlags(bootstrapData?.common?.feature_flags);
|
initFeatureFlags(bootstrapData.common.feature_flags);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isFeatureEnabled(FeatureFlag.ALLOW_DASHBOARD_DOMAIN_SHARDING) &&
|
isFeatureEnabled(FeatureFlag.ALLOW_DASHBOARD_DOMAIN_SHARDING) &&
|
||||||
|
|
|
@ -29,7 +29,7 @@ import { GlobalStyles } from 'src/GlobalStyles';
|
||||||
import ErrorBoundary from 'src/components/ErrorBoundary';
|
import ErrorBoundary from 'src/components/ErrorBoundary';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
import Menu from 'src/views/components/Menu';
|
import Menu from 'src/views/components/Menu';
|
||||||
import { bootstrapData } from 'src/preamble';
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import ToastContainer from 'src/components/MessageToasts/ToastContainer';
|
import ToastContainer from 'src/components/MessageToasts/ToastContainer';
|
||||||
import setupApp from 'src/setup/setupApp';
|
import setupApp from 'src/setup/setupApp';
|
||||||
import setupPlugins from 'src/setup/setupPlugins';
|
import setupPlugins from 'src/setup/setupPlugins';
|
||||||
|
@ -46,10 +46,8 @@ setupApp();
|
||||||
setupPlugins();
|
setupPlugins();
|
||||||
setupExtensions();
|
setupExtensions();
|
||||||
|
|
||||||
const user = { ...bootstrapData.user };
|
const bootstrapData = getBootstrapData();
|
||||||
const menu = {
|
|
||||||
...bootstrapData.common.menu_data,
|
|
||||||
};
|
|
||||||
let lastLocationPathname: string;
|
let lastLocationPathname: string;
|
||||||
|
|
||||||
const boundActions = bindActionCreators({ logEvent }, store.dispatch);
|
const boundActions = bindActionCreators({ logEvent }, store.dispatch);
|
||||||
|
@ -78,13 +76,16 @@ const App = () => (
|
||||||
<LocationPathnameLogger />
|
<LocationPathnameLogger />
|
||||||
<RootContextProviders>
|
<RootContextProviders>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
<Menu data={menu} isFrontendRoute={isFrontendRoute} />
|
<Menu
|
||||||
|
data={bootstrapData.common.menu_data}
|
||||||
|
isFrontendRoute={isFrontendRoute}
|
||||||
|
/>
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(({ path, Component, props = {}, Fallback = Loading }) => (
|
{routes.map(({ path, Component, props = {}, Fallback = Loading }) => (
|
||||||
<Route path={path} key={path}>
|
<Route path={path} key={path}>
|
||||||
<Suspense fallback={<Fallback />}>
|
<Suspense fallback={<Fallback />}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Component user={user} {...props} />
|
<Component user={bootstrapData.user} {...props} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
@ -65,7 +65,7 @@ import setupPlugins from 'src/setup/setupPlugins';
|
||||||
import InfoTooltip from 'src/components/InfoTooltip';
|
import InfoTooltip from 'src/components/InfoTooltip';
|
||||||
import CertifiedBadge from 'src/components/CertifiedBadge';
|
import CertifiedBadge from 'src/components/CertifiedBadge';
|
||||||
import { GenericLink } from 'src/components/GenericLink/GenericLink';
|
import { GenericLink } from 'src/components/GenericLink/GenericLink';
|
||||||
import { bootstrapData } from 'src/preamble';
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import Owner from 'src/types/Owner';
|
import Owner from 'src/types/Owner';
|
||||||
import ChartCard from './ChartCard';
|
import ChartCard from './ChartCard';
|
||||||
|
|
||||||
|
@ -157,6 +157,8 @@ const Actions = styled.div`
|
||||||
color: ${({ theme }) => theme.colors.grayscale.base};
|
color: ${({ theme }) => theme.colors.grayscale.base};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
|
||||||
function ChartList(props: ChartListProps) {
|
function ChartList(props: ChartListProps) {
|
||||||
const {
|
const {
|
||||||
addDangerToast,
|
addDangerToast,
|
||||||
|
@ -224,7 +226,7 @@ function ChartList(props: ChartListProps) {
|
||||||
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);
|
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);
|
||||||
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
|
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
|
||||||
const enableBroadUserAccess =
|
const enableBroadUserAccess =
|
||||||
bootstrapData?.common?.conf?.ENABLE_BROAD_ACTIVITY_ACCESS;
|
bootstrapData.common.conf.ENABLE_BROAD_ACTIVITY_ACCESS;
|
||||||
const handleBulkChartExport = (chartsToExport: Chart[]) => {
|
const handleBulkChartExport = (chartsToExport: Chart[]) => {
|
||||||
const ids = chartsToExport.map(({ id }) => id);
|
const ids = chartsToExport.map(({ id }) => id);
|
||||||
handleResourceExport('chart', ids, () => {
|
handleResourceExport('chart', ids, () => {
|
||||||
|
|
|
@ -49,7 +49,7 @@ import ImportModelsModal from 'src/components/ImportModal/index';
|
||||||
|
|
||||||
import Dashboard from 'src/dashboard/containers/Dashboard';
|
import Dashboard from 'src/dashboard/containers/Dashboard';
|
||||||
import CertifiedBadge from 'src/components/CertifiedBadge';
|
import CertifiedBadge from 'src/components/CertifiedBadge';
|
||||||
import { bootstrapData } from 'src/preamble';
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import DashboardCard from './DashboardCard';
|
import DashboardCard from './DashboardCard';
|
||||||
import { DashboardStatus } from './types';
|
import { DashboardStatus } from './types';
|
||||||
|
|
||||||
|
@ -95,6 +95,8 @@ const Actions = styled.div`
|
||||||
color: ${({ theme }) => theme.colors.grayscale.base};
|
color: ${({ theme }) => theme.colors.grayscale.base};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const bootstrapData = getBootstrapData();
|
||||||
|
|
||||||
function DashboardList(props: DashboardListProps) {
|
function DashboardList(props: DashboardListProps) {
|
||||||
const {
|
const {
|
||||||
addDangerToast,
|
addDangerToast,
|
||||||
|
|
|
@ -24,14 +24,14 @@ import { Provider as ReduxProvider } from 'react-redux';
|
||||||
import { QueryParamProvider } from 'use-query-params';
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
import FlashProvider from '../components/FlashProvider';
|
import FlashProvider from '../components/FlashProvider';
|
||||||
import { bootstrapData, theme } from '../preamble';
|
import { theme } from '../preamble';
|
||||||
import { EmbeddedUiConfigProvider } from '../components/UiConfigContext';
|
import { EmbeddedUiConfigProvider } from '../components/UiConfigContext';
|
||||||
import { DynamicPluginProvider } from '../components/DynamicPlugins';
|
import { DynamicPluginProvider } from '../components/DynamicPlugins';
|
||||||
|
|
||||||
const common = { ...bootstrapData.common };
|
const { common } = getBootstrapData();
|
||||||
|
|
||||||
const extensionsRegistry = getExtensionsRegistry();
|
const extensionsRegistry = getExtensionsRegistry();
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import createCache from '@emotion/cache';
|
||||||
import { ThemeProvider } from '@superset-ui/core';
|
import { ThemeProvider } from '@superset-ui/core';
|
||||||
import Menu from 'src/views/components/Menu';
|
import Menu from 'src/views/components/Menu';
|
||||||
import { theme } from 'src/preamble';
|
import { theme } from 'src/preamble';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
|
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { setupStore } from './store';
|
import { setupStore } from './store';
|
||||||
|
@ -33,10 +34,8 @@ import { setupStore } from './store';
|
||||||
// Disable connecting to redux debugger so that the React app injected
|
// Disable connecting to redux debugger so that the React app injected
|
||||||
// Below the menu like SqlLab or Explore can connect its redux store to the debugger
|
// Below the menu like SqlLab or Explore can connect its redux store to the debugger
|
||||||
const store = setupStore(true);
|
const store = setupStore(true);
|
||||||
const container = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrapJson = container?.getAttribute('data-bootstrap') ?? '{}';
|
const menu = { ...bootstrapData.common.menu_data };
|
||||||
const bootstrap = JSON.parse(bootstrapJson);
|
|
||||||
const menu = { ...bootstrap.common.menu_data };
|
|
||||||
|
|
||||||
const emotionCache = createCache({
|
const emotionCache = createCache({
|
||||||
key: 'menu',
|
key: 'menu',
|
||||||
|
|
|
@ -48,10 +48,12 @@ import {
|
||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
import {
|
import {
|
||||||
BootstrapUser,
|
BootstrapUser,
|
||||||
|
UndefinedUser,
|
||||||
UserWithPermissionsAndRoles,
|
UserWithPermissionsAndRoles,
|
||||||
} from 'src/types/bootstrapTypes';
|
} from 'src/types/bootstrapTypes';
|
||||||
import { AnyDatasourcesAction } from 'src/explore/actions/datasourcesActions';
|
import { AnyDatasourcesAction } from 'src/explore/actions/datasourcesActions';
|
||||||
import { HydrateExplore } from 'src/explore/actions/hydrateExplore';
|
import { HydrateExplore } from 'src/explore/actions/hydrateExplore';
|
||||||
|
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||||
import { Dataset } from '@superset-ui/chart-controls';
|
import { Dataset } from '@superset-ui/chart-controls';
|
||||||
|
|
||||||
// Some reducers don't do anything, and redux is just used to reference the initial "state".
|
// Some reducers don't do anything, and redux is just used to reference the initial "state".
|
||||||
|
@ -61,8 +63,7 @@ const noopReducer =
|
||||||
(state: STATE = initialState) =>
|
(state: STATE = initialState) =>
|
||||||
state;
|
state;
|
||||||
|
|
||||||
const container = document.getElementById('app');
|
const bootstrapData = getBootstrapData();
|
||||||
const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}');
|
|
||||||
|
|
||||||
export const USER_LOADED = 'USER_LOADED';
|
export const USER_LOADED = 'USER_LOADED';
|
||||||
|
|
||||||
|
@ -72,9 +73,9 @@ export type UserLoadedAction = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const userReducer = (
|
const userReducer = (
|
||||||
user: BootstrapUser = bootstrap.user || {},
|
user = bootstrapData.user || {},
|
||||||
action: UserLoadedAction,
|
action: UserLoadedAction,
|
||||||
): BootstrapUser => {
|
): BootstrapUser | UndefinedUser => {
|
||||||
if (action.type === USER_LOADED) {
|
if (action.type === USER_LOADED) {
|
||||||
return action.user;
|
return action.user;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +105,7 @@ const CombinedDatasourceReducers = (
|
||||||
// exported for tests
|
// exported for tests
|
||||||
export const rootReducer = combineReducers({
|
export const rootReducer = combineReducers({
|
||||||
messageToasts: messageToastReducer,
|
messageToasts: messageToastReducer,
|
||||||
common: noopReducer(bootstrap.common || {}),
|
common: noopReducer(bootstrapData.common),
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
impressionId: noopReducer(shortid.generate()),
|
impressionId: noopReducer(shortid.generate()),
|
||||||
charts,
|
charts,
|
||||||
|
|
|
@ -115,6 +115,7 @@ FRONTEND_CONF_KEYS = (
|
||||||
"HTML_SANITIZATION",
|
"HTML_SANITIZATION",
|
||||||
"HTML_SANITIZATION_SCHEMA_EXTENSIONS",
|
"HTML_SANITIZATION_SCHEMA_EXTENSIONS",
|
||||||
"WELCOME_PAGE_LAST_TAB",
|
"WELCOME_PAGE_LAST_TAB",
|
||||||
|
"VIZ_TYPE_DENYLIST",
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
Loading…
Reference in New Issue