blob: a4235d20112240daea524f1a980824a8e4134aa5 [file] [log] [blame]
/**
* 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 React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Well } from 'react-bootstrap';
import ProgressBar from 'src/common/components/ProgressBar';
import Label from 'src/components/Label';
import { t } from '@superset-ui/core';
import TableView from 'src/components/TableView';
import Button from 'src/components/Button';
import { fDuration } from 'src/modules/dates';
import { IconTooltip } from '../../components/IconTooltip';
import ResultSet from './ResultSet';
import ModalTrigger from '../../components/ModalTrigger';
import HighlightedSql from './HighlightedSql';
import QueryStateLabel from './QueryStateLabel';
const propTypes = {
columns: PropTypes.array,
actions: PropTypes.object,
queries: PropTypes.array,
onUserClicked: PropTypes.func,
onDbClicked: PropTypes.func,
displayLimit: PropTypes.number.isRequired,
};
const defaultProps = {
columns: ['started', 'duration', 'rows'],
queries: [],
onUserClicked: () => {},
onDbClicked: () => {},
};
const openQuery = id => {
const url = `/superset/sqllab?queryId=${id}`;
window.open(url);
};
const QueryTable = props => {
const columns = useMemo(
() =>
props.columns.map(column => ({
accessor: column,
Header: column,
disableSortBy: true,
})),
[props.columns],
);
const data = useMemo(() => {
const restoreSql = query => {
props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
};
const openQueryInNewTab = query => {
props.actions.cloneQueryToNewTab(query, true);
};
const openAsyncResults = (query, displayLimit) => {
props.actions.fetchQueryResults(query, displayLimit);
};
const clearQueryResults = query => {
props.actions.clearQueryResults(query);
};
const removeQuery = query => {
props.actions.removeQuery(query);
};
return props.queries
.map(query => {
const q = { ...query };
if (q.endDttm) {
q.duration = fDuration(q.startDttm, q.endDttm);
}
const time = moment(q.startDttm).format().split('T');
q.time = (
<div>
<span>
{time[0]} <br /> {time[1]}
</span>
</div>
);
q.user = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={() => props.onUserClicked(q.userId)}
>
{q.user}
</Button>
);
q.db = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={() => props.onDbClicked(q.dbId)}
>
{q.db}
</Button>
);
q.started = moment(q.startDttm).format('HH:mm:ss');
q.querylink = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={() => openQuery(q.queryId)}
>
<i className="fa fa-external-link m-r-3" />
{t('Edit')}
</Button>
);
q.sql = (
<Well>
<HighlightedSql
sql={q.sql}
rawSql={q.executedSql}
shrink
maxWidth={60}
/>
</Well>
);
if (q.resultsKey) {
q.output = (
<ModalTrigger
className="ResultsModal"
triggerNode={
<Label type="info" className="pointer">
{t('View results')}
</Label>
}
modalTitle={t('Data preview')}
beforeOpen={() => openAsyncResults(query, props.displayLimit)}
onExit={() => clearQueryResults(query)}
modalBody={
<ResultSet
showSql
query={query}
actions={props.actions}
height={400}
displayLimit={props.displayLimit}
/>
}
responsive
/>
);
} else {
// if query was run using ctas and force_ctas_schema was set
// tempTable will have the schema
const schemaUsed =
q.ctas && q.tempTable && q.tempTable.includes('.') ? '' : q.schema;
q.output = [schemaUsed, q.tempTable].filter(v => v).join('.');
}
q.progress = (
<ProgressBar percent={parseInt(q.progress.toFixed(0), 10)} striped />
);
let errorTooltip;
if (q.errorMessage) {
errorTooltip = (
<IconTooltip tooltip={q.errorMessage}>
<i className="fa fa-exclamation-circle text-danger" />
</IconTooltip>
);
}
q.state = (
<div>
<QueryStateLabel query={query} />
{errorTooltip}
</div>
);
q.actions = (
<div>
<IconTooltip
className="fa fa-pencil m-r-3 pointer"
onClick={() => restoreSql(query)}
tooltip={t(
'Overwrite text in the editor with a query on this table',
)}
placement="top"
/>
<IconTooltip
className="fa fa-plus-circle m-r-3 pointer"
onClick={() => openQueryInNewTab(query)}
tooltip={t('Run query in a new tab')}
placement="top"
/>
<IconTooltip
className="fa fa-trash m-r-3 pointer"
tooltip={t('Remove query from log')}
onClick={() => removeQuery(query)}
/>
</div>
);
return q;
})
.reverse();
}, [props]);
return (
<div className="QueryTable">
<TableView
columns={columns}
data={data}
className="table-condensed"
pageSize={50}
/>
</div>
);
};
QueryTable.propTypes = propTypes;
QueryTable.defaultProps = defaultProps;
export default QueryTable;