/** * 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 { FC, KeyboardEvent, MouseEvent, useCallback, useEffect, useState, } from 'react'; import { useSelector } from 'react-redux'; import rison from 'rison'; import { t } from '@apache-superset/core/translation'; import { SupersetClient } from '@superset-ui/core'; import { styled, useTheme } from '@apache-superset/core/theme'; import { Icons, Switch, Button, Skeleton, Card, Space, } from '@superset-ui/core/components'; import { CopyToClipboard } from 'src/components'; import { RootState } from 'src/dashboard/types'; import { findPermission } from 'src/utils/findPermission'; import { makeUrl } from 'src/utils/pathUtils'; import CodeSyntaxHighlighter, { SupportedLanguage, preloadLanguages, } from '@superset-ui/core/components/CodeSyntaxHighlighter'; import { useHistory } from 'react-router-dom'; import { ExplorePageState } from 'src/explore/types'; export interface ViewQueryProps { sql: string; datasource: string; language?: SupportedLanguage; } const StyledSyntaxContainer = styled.div` height: 100%; display: flex; flex-direction: column; `; const StyledThemedSyntaxHighlighter = styled(CodeSyntaxHighlighter)` flex: 1; `; const StyledFooter = styled.div` display: flex; justify-content: space-between; align-items: center; `; const DATASET_BACKEND_QUERY = { keys: ['none'], columns: ['database.backend'], }; const ViewQuery: FC = props => { const { sql, language = 'sql', datasource } = props; const theme = useTheme(); const datasetId = datasource?.split('__')[0]; const exploreBackend = useSelector( (state: ExplorePageState) => state.explore?.datasource?.database?.backend, ); const [formattedSQL, setFormattedSQL] = useState(); const [showFormatSQL, setShowFormatSQL] = useState(true); const history = useHistory(); const currentSQL = (showFormatSQL ? formattedSQL : sql) ?? sql; const canAccessSQLLab = useSelector((state: RootState) => findPermission('menu_access', 'SQL Lab', state.user?.roles), ); // Preload the language when component mounts to ensure smooth experience useEffect(() => { preloadLanguages([language]); }, [language]); const formatCurrentQuery = useCallback(async () => { if (formattedSQL) { setShowFormatSQL(val => !val); return; } try { let backend = exploreBackend; // Fetch backend info if not available in Redux state if (!backend) { const queryParams = rison.encode(DATASET_BACKEND_QUERY); const response = await SupersetClient.get({ endpoint: `/api/v1/dataset/${datasetId}?q=${queryParams}`, }); const { backend: datasetBackend } = response.json.result.database; backend = datasetBackend; } // Format the SQL query const formatResponse = await SupersetClient.post({ endpoint: `/api/v1/sqllab/format_sql/`, body: JSON.stringify({ sql, engine: backend, }), headers: { 'Content-Type': 'application/json' }, }); setFormattedSQL(formatResponse.json.result); setShowFormatSQL(true); } catch (error) { setShowFormatSQL(false); } }, [sql, datasetId, formattedSQL]); const navToSQLLab = useCallback( (domEvent: KeyboardEvent | MouseEvent) => { const requestedQuery = { datasourceKey: datasource, sql: currentSQL, }; if (domEvent.metaKey || domEvent.ctrlKey) { domEvent.preventDefault(); window.open( makeUrl( `/sqllab?datasourceKey=${datasource}&sql=${encodeURIComponent(currentSQL)}`, ), '_blank', ); } else { history.push({ pathname: '/sqllab', state: { requestedQuery } }); } }, [history, datasource, currentSQL], ); useEffect(() => { formatCurrentQuery(); }, [sql]); return ( {!formattedSQL && showFormatSQL ? ( ) : ( {currentSQL} )} } > {t('Copy')} } /> {canAccessSQLLab && ( )} ); }; export default ViewQuery;